/// <summary> /// /// </summary> /// <param name="bundle"></param> /// <param name="keySignature"></param> /// <param name="index"></param> /// <param name="length"></param> /// <param name="curl"></param> // Takes an input `signature`, and writes its digest out to the first 243 trits public static void DigestBundleSignature(sbyte[] bundle, sbyte[] keySignature, int index, int length, ICurl curl) { if (bundle.Length != Constants.HashLength) { throw new ArgumentException("bundle are not equal HashLength!"); } int totalLength = Constants.KeyLength * CheckSumSecurity(bundle); if (totalLength != length) { throw new ArgumentException("Key space size must be equal to security space size"); } for (int i = 0; i < length / Constants.HashLength; i++) { int maxCount = (bundle[i * Constants.TryteWidth] + bundle[i * Constants.TryteWidth + 1] * 3 + bundle[i * Constants.TryteWidth + 2] * 9) - Constants.MinTryteValue; for (int n = 0; n < maxCount; n++) { curl.Reset(); curl.Absorb(keySignature, index + i * Constants.HashLength, Constants.HashLength); Array.Copy(curl.State, 0, keySignature, index + i * Constants.HashLength, Constants.HashLength); } } curl.Reset(); curl.Absorb(keySignature, index, length); }
/// <summary> /// /// </summary> /// <param name="payload"></param> /// <param name="payloadIndex"></param> /// <param name="payloadLength"></param> /// <param name="key"></param> /// <param name="keyIndex"></param> /// <param name="keyLength"></param> /// <param name="curl"></param> public static void UnMask( sbyte[] payload, int payloadIndex, int payloadLength, sbyte[] key, int keyIndex, int keyLength, ICurl curl) { curl.Absorb(key, keyIndex, keyLength); var keyChunk = new sbyte[Constants.HashLength]; curl.Squeeze(keyChunk); for (var i = 0; i < payloadLength; i += Constants.HashLength) { var left = payloadLength - i; var length = left > Constants.HashLength ? Constants.HashLength : left; for (var n = 0; n < length; n++) { keyChunk[n] = TritsHelper.Sum(payload[payloadIndex + i + n], (sbyte)-keyChunk[n]); } Array.Copy(keyChunk, 0, payload, payloadIndex + i, length); curl.Absorb(keyChunk, 0, length); Array.Copy(curl.Rate, keyChunk, length); } }
// root public static int Root(sbyte[] address, sbyte[] hashes, int index, ICurl curl) { int i = 1; int numBeforeEnd = 0; sbyte[] outTrits = new sbyte[Constants.HashLength]; Array.Copy(address, outTrits, Constants.HashLength); for (int n = 0; n < hashes.Length / Constants.HashLength; n++) { curl.Reset(); if ((i & index) == 0) { numBeforeEnd += 1; curl.Absorb(outTrits); curl.Absorb(hashes, n * Constants.HashLength, Constants.HashLength); } else { curl.Absorb(hashes, n * Constants.HashLength, Constants.HashLength); curl.Absorb(outTrits); } i <<= 1; Array.Copy(curl.Rate, outTrits, Constants.HashLength); } return(numBeforeEnd); }
/// <summary> /// </summary> /// <param name="digests">digest trytes</param> public void AddAddressDigest(string[] digests) { foreach (var digest in digests) { // Get trits of digest var digestTrits = Converter.ToTrits(digest); // Absorb digest _curl.Absorb(digestTrits, 0, digestTrits.Length); } }
/** * @param inSeed * @param index * @param security * @return * @throws ArgumentException is thrown when the specified security level is not valid. */ public int[] Key(int[] inSeed, int index, int security) { if (security < 1) { throw new ArgumentException(Constants.INVALID_SECURITY_LEVEL_INPUT_ERROR); } int[] seed = (int[])inSeed.Clone(); // Derive subseed. for (int i = 0; i < index; i++) { for (int j = 0; j < seed.Length; j++) { if (++seed[j] > 1) { seed[j] = -1; } else { break; } } } ICurl curl = this.GetICurlObject(); curl.Reset(); curl.Absorb(seed, 0, seed.Length); // seed[0..HASH_LENGTH] contains subseed curl.Squeeze(seed, 0, seed.Length); curl.Reset(); // absorb subseed curl.Absorb(seed, 0, seed.Length); int[] key = new int[security * Curl.HashLength * NUMBER_OF_ROUNDS]; //TODO is this not 81!! int[] buffer = new int[seed.Length]; int offset = 0; while (security-- > 0) { for (int i = 0; i < NUMBER_OF_ROUNDS; i++) { curl.Squeeze(buffer, 0, seed.Length); Array.Copy(buffer, 0, key, offset, Curl.HashLength); offset += Curl.HashLength; } } return(key); }
/// <summary> /// /// </summary> /// <param name="seed"></param> /// <param name="index"></param> /// <param name="curl"></param> /// <returns></returns> public static sbyte[] Subseed(sbyte[] seed, int index, ICurl curl) { sbyte[] subseed = new sbyte[Constants.HashLength]; int copyLength = Math.Min(seed.Length, subseed.Length); Array.Copy(seed, subseed, copyLength); for (var i = 0; i < index; i++) { for (var j = 0; j < Constants.HashLength; j++) { if (++subseed[j] > 1) { subseed[j] = -1; } else { break; } } } curl.Absorb(subseed); // curl.absorb(&out[0..seed.len()]); curl.Squeeze(subseed); // curl.squeeze(&mut out[0..HASH_LENGTH]); curl.Reset(); return(subseed); }
/// <summary> /// /// </summary> /// <param name="subseed"></param> /// <param name="security"></param> /// <param name="c1"></param> /// <param name="c2"></param> /// <param name="c3"></param> /// <returns></returns> public static sbyte[] SubseedToDigest( sbyte[] subseed, int security, ICurl c1, ICurl c2, ICurl c3) { sbyte[] digest = new sbyte[Constants.HashLength]; int length = security * Constants.KeyLength / Constants.HashLength; c1.Absorb(subseed); for (int i = 0; i < length; i++) { c1.Squeeze(digest); for (int n = 0; n < 27; n++) { c2.Reset(); c2.Absorb(digest); Array.Copy(c2.State, digest, Constants.HashLength); } c3.Absorb(digest); } c3.Squeeze(digest); return(digest); }
public int[] Digest(int[] normalizedBundleFragment, int[] signatureFragment) { this.curl.Reset(); ICurl curl = this.GetICurlObject(); int[] buffer = new int[Curl.HashLength]; for (int i = 0; i < NUMBER_OF_ROUNDS; i++) { Array.Copy(signatureFragment, i * Curl.HashLength, buffer, 0, Curl.HashLength); // buffer = Array.Copy(signatureFragment, i * Curl.HashLength, buffer, (i + 1) * Curl.HashLength, (i + 1) * NUMBER_OF_ROUNDS); for (int j = normalizedBundleFragment[i] + 13; j-- > 0;) { curl.Reset(); curl.Absorb(buffer); curl.Squeeze(buffer); } this.curl.Absorb(buffer); } this.curl.Squeeze(buffer); return(buffer); }
public int[] Digests(int[] key) { int security = (int)Math.Floor((double)key.Length / KEY_LENGTH); int[] digests = new int[security * Curl.HashLength]; int[] keyFragment = new int[KEY_LENGTH]; ICurl curl = this.GetICurlObject(); for (int i = 0; i < Math.Floor((double)key.Length / KEY_LENGTH); i++) { Array.Copy(key, i * KEY_LENGTH, keyFragment, 0, KEY_LENGTH); //System.arraycopy(key, i * KEY_LENGTH, keyFragment, 0, KEY_LENGTH); for (int j = 0; j < NUMBER_OF_ROUNDS; j++) { for (int k = 0; k < 26; k++) { curl.Reset() .Absorb(keyFragment, j * Curl.HashLength, Curl.HashLength) .Squeeze(keyFragment, j * Curl.HashLength, Curl.HashLength); } } curl.Reset(); curl.Absorb(keyFragment, 0, keyFragment.Length); curl.Squeeze(digests, i * Curl.HashLength, Curl.HashLength); } return(digests); }
/// <summary> /// Finalizes the bundle using the specified curl implementation /// </summary> /// <param name="customCurl">The custom curl.</param> public void FinalizeBundle(ICurl customCurl) { customCurl.Reset(); for (int i = 0; i < Transactions.Count; i++) { int[] valueTrits = Converter.ToTrits(Transactions[i].Value, 81); int[] timestampTrits = Converter.ToTrits(Transactions[i].Timestamp, 27); int[] currentIndexTrits = Converter.ToTrits(Transactions[i].CurrentIndex = ("" + i), 27); int[] lastIndexTrits = Converter.ToTrits( Transactions[i].LastIndex = ("" + (this.Transactions.Count - 1)), 27); string stringToConvert = Transactions[i].Address + Converter.ToTrytes(valueTrits) + Transactions[i].Tag + Converter.ToTrytes(timestampTrits) + Converter.ToTrytes(currentIndexTrits) + Converter.ToTrytes(lastIndexTrits); int[] t = Converter.ToTrits(stringToConvert); customCurl.Absorb(t, 0, t.Length); } int[] hash = new int[243]; customCurl.Squeeze(hash, 0, hash.Length); string hashInTrytes = Converter.ToTrytes(hash); for (int i = 0; i < Transactions.Count; i++) { Transactions[i].Bundle = hashInTrytes; } }
/// <summary> /// Finalizes the bundle using the specified curl implementation /// </summary> /// <param name="customCurl">The custom curl.</param> public void FinalizeBundle(ICurl customCurl) { string hashInTrytes = string.Empty; bool validBundle = false; while (!validBundle) { customCurl.Reset(); for (var i = 0; i < Transactions.Count; i++) { var valueTrits = Converter.ToTrits(Transactions[i].Value, 81); var timestampTrits = Converter.ToTrits(Transactions[i].Timestamp, 27); var currentIndexTrits = Converter.ToTrits(Transactions[i].CurrentIndex = i, 27); var lastIndexTrits = Converter.ToTrits( Transactions[i].LastIndex = Transactions.Count - 1, 27); var stringToConvert = Transactions[i].Address + Converter.ToTrytes(valueTrits) + Transactions[i].ObsoleteTag + Converter.ToTrytes(timestampTrits) + Converter.ToTrytes(currentIndexTrits) + Converter.ToTrytes(lastIndexTrits); var t = Converter.ToTrits(stringToConvert); customCurl.Absorb(t, 0, t.Length); } var hash = new sbyte[Sponge.HASH_LENGTH]; customCurl.Squeeze(hash, 0, hash.Length); hashInTrytes = Converter.ToTrytes(hash); bool foundValue = false; var normalizedHash = NormalizedBundle(hashInTrytes); foreach (var normalizedHashValue in normalizedHash) { if (normalizedHashValue == 13 /* = M */) { foundValue = true; // Insecure bundle. Increment Tag and recompute bundle hash. var obsoleteTagTrits = Converter.ToTrits(Transactions[0].ObsoleteTag); Converter.Increment(obsoleteTagTrits, 81); Transactions[0].ObsoleteTag = Converter.ToTrytes(obsoleteTagTrits); break; } } validBundle = !foundValue; } foreach (var transaction in Transactions) { transaction.Bundle = hashInTrytes; } }
/// <summary> /// /// </summary> /// <param name="digest"></param> /// <param name="curl"></param> /// <returns></returns> public static sbyte[] Address(sbyte[] digest, ICurl curl) { sbyte[] address = new sbyte[Constants.AddressLength]; curl.Absorb(digest); curl.Squeeze(address); curl.Reset(); return(address); }
public int[] Key(int[] seed, int index, int length) { int[] subseed = seed; for (int i = 0; i < index; i++) { for (int j = 0; j < 243; j++) { if (++subseed[j] > 1) { subseed[j] = -1; } else { break; } } } curl.Reset(); curl.Absorb(subseed, 0, subseed.Length); curl.Squeeze(subseed, 0, subseed.Length); curl.Reset(); curl.Absorb(subseed, 0, subseed.Length); IList <int> key = new List <int>(); int[] buffer = new int[subseed.Length]; int offset = 0; while (length-- > 0) { for (int i = 0; i < 27; i++) { curl.Squeeze(buffer, offset, buffer.Length); for (int j = 0; j < 243; j++) { key.Add(buffer[j]); } } } return(ToIntArray(key)); }
private static string CalculateChecksum(string address) { ICurl curl = SpongeFactory.Create(SpongeFactory.Mode.KERL); curl.Reset(); curl.Absorb(Converter.ToTrits(address)); sbyte[] checksumTrits = new sbyte[Sponge.HASH_LENGTH]; curl.Squeeze(checksumTrits); string checksum = Converter.ToTrytes(checksumTrits); return(checksum.Substring(72, 9)); }
/// <summary> /// /// </summary> /// <param name="trunkTransaction"></param> /// <param name="branchTransaction"></param> /// <param name="minWeightMagnitude"></param> /// <param name="trytes"></param> /// <returns></returns> public AttachToTangleResponse AttachToTangle( string trunkTransaction, string branchTransaction, int minWeightMagnitude, string[] trytes) { var response = new AttachToTangleResponse { Trytes = new List <string>() }; string previousTransaction = null; foreach (var t in trytes) { var txn = new Transaction(t) { TrunkTransaction = previousTransaction ?? trunkTransaction, BranchTransaction = previousTransaction == null ? branchTransaction : trunkTransaction }; if (string.IsNullOrEmpty(txn.Tag) || Regex.IsMatch(txn.Tag, "9*")) { txn.Tag = txn.ObsoleteTag; } txn.AttachmentTimestamp = TimeStamp.Now(); txn.AttachmentTimestampLowerBound = 0; txn.AttachmentTimestampUpperBound = 3_812_798_742_493L; // POW var transactionTrits = Converter.ToTrits(txn.ToTrytes()); if (!_pearlDiver.Search(transactionTrits, minWeightMagnitude, 0)) { throw new IllegalStateException("PearlDiver search failed"); } // Hash var hash = new sbyte[Sponge.HASH_LENGTH]; ICurl curl = SpongeFactory.Create(SpongeFactory.Mode.CURLP81); curl.Reset(); curl.Absorb(transactionTrits); curl.Squeeze(hash); previousTransaction = Converter.ToTrytes(hash); response.Trytes.Add(Converter.ToTrytes(transactionTrits)); } response.Trytes.Reverse(); return(response); }
private static MerkleNode Combine( sbyte[] seed, int index, uint count, uint remainingWidth, int security, ICurl c1, ICurl c2, ICurl c3) { uint rightCount = NextPowerOfTwo(count) >> 1; uint leftCount = count - rightCount; var left = CreateMerkleNode(seed, index, leftCount, remainingWidth >> 1, security, c1, c2, c3); var right = CreateMerkleNode(seed, (int)(index + leftCount), rightCount, remainingWidth >> 1, security, c1, c2, c3); if (left != null && (left.IsLeaf || left.IsFullNode)) { c1.Absorb(left.Hash); } else { c1.Absorb(NullHash); } if (right != null && (right.IsLeaf || right.IsFullNode)) { c1.Absorb(right.Hash); } else { c1.Absorb(NullHash); } sbyte[] hash = new sbyte[Constants.HashLength]; c1.Squeeze(hash, 0, hash.Length); c1.Reset(); return(new MerkleNode(left, hash, right)); }
/// <summary> /// /// </summary> /// <param name="keySpace"></param> /// <param name="index"></param> /// <param name="length"></param> /// <param name="security"></param> /// <param name="curl"></param> // Take first 243 trits of key_space as subseed, and write key out to key_space public static void Key(sbyte[] keySpace, int index, int length, int security, ICurl curl) { int totalLength = security * Constants.KeyLength; if (totalLength != length) { throw new ArgumentException("Key space size must be equal to security space size"); } curl.Absorb(keySpace, index, Constants.HashLength); curl.Squeeze(keySpace, index, length); for (int divOffset = 0; divOffset < length / Constants.HashLength; divOffset++) { int offset = divOffset * Constants.HashLength; curl.Reset(); curl.Absorb(keySpace, index + offset, Constants.HashLength); Array.Copy(curl.State, 0, keySpace, index + offset, Constants.HashLength); } curl.Reset(); }
public static string FinalizeBundleHash(this IEnumerable <TransactionItem> transactionItems, ICurl customCurl) { customCurl.Reset(); var transactionCount = transactionItems.Count(); bool valid = false; string hashInTrytes = ""; while (!valid) { for (int i = 0; i < transactionCount; i++) { var transaction = transactionItems.ElementAt(i); transaction.CurrentIndex = i; transaction.LastIndex = transactionCount - 1; var trytes = transaction.GetBundleTrytes(); int[] t = Converter.ToTrits(trytes); customCurl.Absorb(t, 0, t.Length); } int[] hash = new int[243]; customCurl.Squeeze(hash, 0, hash.Length); hashInTrytes = Converter.ToTrytes(hash); bool found = false; var normalizedBundleValues = NormalizedBundle(hashInTrytes); foreach (int normalizedBundleValue in normalizedBundleValues) { if (normalizedBundleValue == 13) { found = true; var obsoleteTagTrits = Converter.ToTritsString(transactionItems.First().ObsoleteTag); Converter.Increment(obsoleteTagTrits, 81); transactionItems.First().ObsoleteTag = Converter.ToTrytes(obsoleteTagTrits); customCurl.Reset(); } } valid = !found; } foreach (var transaction in transactionItems) { transaction.Bundle = hashInTrytes; } return(hashInTrytes); }
/// <summary> /// </summary> /// <param name="seed"></param> /// <param name="index"></param> /// <param name="security"></param> /// <returns></returns> public int[] Key(int[] seed, int index, int security) { var subseed = new int[seed.Length]; seed.CopyTo(subseed, 0); for (var i = 0; i < index; i++) { for (var j = 0; j < 243; j++) { if (++subseed[j] > 1) { subseed[j] = -1; } else { break; } } } _curl.Reset(); _curl.Absorb(subseed, 0, subseed.Length); _curl.Squeeze(subseed, 0, subseed.Length); _curl.Reset(); _curl.Absorb(subseed, 0, subseed.Length); var key = new List <int>(); var buffer = new int[subseed.Length]; var offset = 0; while (security-- > 0) { for (var i = 0; i < 27; i++) { _curl.Squeeze(buffer, offset, buffer.Length); for (var j = 0; j < 243; j++) { key.Add(buffer[j]); } } } return(key.ToArray()); }
/// <summary> /// /// </summary> /// <param name="payload"></param> /// <param name="payloadIndex"></param> /// <param name="payloadLength"></param> /// <param name="curl"></param> public static void UnMaskSlice(sbyte[] payload, int payloadIndex, int payloadLength, ICurl curl) { var keyChunk = new int[Constants.HashLength]; for (var i = 0; i < payloadLength; i += Constants.HashLength) { var left = payloadLength - i; var length = left > Constants.HashLength ? Constants.HashLength : left; Array.Copy(curl.Rate, keyChunk, length); for (var n = 0; n < length; n++) { payload[payloadIndex + i + n] = TritsHelper.Sum(payload[payloadIndex + i + n], (sbyte)-keyChunk[n]); } curl.Absorb(payload, payloadIndex + i, length); } }
/// <summary> /// Initializes a new instance of the <see cref="Transaction" /> class. /// </summary> /// <param name="trytes">The trytes representing the transaction</param> /// <param name="curl">The curl implementation.</param> /// <exception cref="System.ArgumentException"> /// trytes must non-null /// or /// position " + i + "must not be '9' /// </exception> public Transaction(string trytes, ICurl curl) { if (string.IsNullOrEmpty(trytes)) { throw new ArgumentException("trytes must non-null"); } // validity check for (var i = 2279; i < 2295; i++) { if (trytes[i] != '9') { throw new ArgumentException("position " + i + "must not be '9'"); } } var transactionTrits = Converter.ToTrits(trytes); var hash = new int[243]; // generate the correct transaction hash curl.Reset(); curl.Absorb(transactionTrits, 0, transactionTrits.Length); curl.Squeeze(hash, 0, hash.Length); Hash = Converter.ToTrytes(hash); SignatureMessageFragment = trytes.Substring(0, 2187); Address = trytes.Substring(2187, 2268 - 2187); Value = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6804, 6837)); ObsoleteTag = trytes.Substring(2295, 2322 - 2295); Timestamp = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6966, 6993)); CurrentIndex = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 6993, 7020)); LastIndex = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7020, 7047)); Bundle = trytes.Substring(2349, 2430 - 2349); TrunkTransaction = trytes.Substring(2430, 2511 - 2430); BranchTransaction = trytes.Substring(2511, 2592 - 2511); Tag = trytes.Substring(2592, 2619 - 2592); AttachmentTimestamp = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7857, 7884)); AttachmentTimestampLowerBound = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7884, 7911)); AttachmentTimestampUpperBound = Converter.ToLongValue(ArrayUtils.SubArray(transactionTrits, 7911, 7938)); Nonce = trytes.Substring(2646, 2673 - 2646); }
public static string FinalizeBundleHash(this IEnumerable <TransactionItem> transactionItems, ICurl customCurl) { customCurl.Reset(); var transactionCount = transactionItems.Count(); for (int i = 0; i < transactionCount; i++) { var transaction = transactionItems.ElementAt(i); int[] valueTrits = Converter.ToTrits(transaction.Value, 81); int[] timestampTrits = Converter.ToTrits(transaction.Timestamp, 27); int[] currentIndexTrits = Converter.ToTrits(transaction.CurrentIndex = ("" + i), 27); int[] lastIndexTrits = Converter.ToTrits( transaction.LastIndex = ("" + (transactionCount - 1)), 27); string stringToConvert = transaction.Address + Converter.ToTrytes(valueTrits) + transaction.Tag + Converter.ToTrytes(timestampTrits) + Converter.ToTrytes(currentIndexTrits) + Converter.ToTrytes(lastIndexTrits); int[] t = Converter.ToTrits(stringToConvert); customCurl.Absorb(t, 0, t.Length); } int[] hash = new int[243]; customCurl.Squeeze(hash, 0, hash.Length); string hashInTrytes = Converter.ToTrytes(hash); foreach (var transaction in transactionItems) { transaction.Bundle = hashInTrytes; } return(hashInTrytes); }
public int[] Digest(int[] normalizedBundleFragment, int[] signatureFragment) { curl.Reset(); int[] buffer = new int[243]; for (int i = 0; i < 27; i++) { buffer = ArrayUtils.SubArray(signatureFragment, i * 243, 243); ; ICurl jCurl = curl.Clone(); for (int j = normalizedBundleFragment[i] + 13; j-- > 0;) { jCurl.Reset(); jCurl.Absorb(buffer); jCurl.Squeeze(buffer); } curl.Absorb(buffer); } curl.Squeeze(buffer); return(buffer); }
/// <summary> /// Finalizes the bundle using the specified curl implementation /// </summary> /// <param name="customCurl">The custom curl.</param> public void FinalizeBundle(ICurl customCurl) { customCurl.Reset(); for (var i = 0; i < Transactions.Count; i++) { var valueTrits = Converter.ToTrits(Transactions[i].Value, 81); var timestampTrits = Converter.ToTrits(Transactions[i].Timestamp, 27); var currentIndexTrits = Converter.ToTrits(Transactions[i].CurrentIndex = i, 27); var lastIndexTrits = Converter.ToTrits( Transactions[i].LastIndex = Transactions.Count - 1, 27); var stringToConvert = Transactions[i].Address + Converter.ToTrytes(valueTrits) + Transactions[i].Tag + Converter.ToTrytes(timestampTrits) + Converter.ToTrytes(currentIndexTrits) + Converter.ToTrytes(lastIndexTrits); var t = Converter.ToTrits(stringToConvert); customCurl.Absorb(t, 0, t.Length); } var hash = new int[243]; customCurl.Squeeze(hash, 0, hash.Length); var hashInTrytes = Converter.ToTrytes(hash); foreach (var transaction in Transactions) { transaction.Bundle = hashInTrytes; } }
/// <summary> /// /// </summary> /// <param name="bundle"></param> /// <param name="curl"></param> /// <returns></returns> public static bool IsBundle(Bundle bundle, ICurl curl = null) { if (curl == null) { curl = SpongeFactory.Create(SpongeFactory.Mode.KERL); } long totalSum = 0; int lastIndex = bundle.Length - 1; for (int i = 0; i < bundle.Length; i++) { var tx = bundle.Transactions[i]; totalSum += tx.Value; if (tx.CurrentIndex != i) { throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR); } if (tx.LastIndex != lastIndex) { throw new ArgumentException(Constants.INVALID_BUNDLE_ERROR); } sbyte[] txTrits = Converter.ToTrits(tx.ToTrytes().Substring(2187, 162)); curl.Absorb(txTrits); // continue if output or signature tx if (tx.Value >= 0) { continue; } // here we have an input transaction (negative value) List <string> fragments = new List <string> { tx.SignatureMessageFragment }; // find the subsequent txs containing the remaining signature // message fragments for this input transaction for (int j = i; j < bundle.Length - 1; j++) { Transaction tx2 = bundle.Transactions[j + 1]; // check if the tx is part of the input transaction if (tx.Address.Equals(tx2.Address, StringComparison.Ordinal) && tx2.Value == 0) { // append the signature message fragment fragments.Add(tx2.SignatureMessageFragment); } } bool valid = new Signing(curl.Clone()).ValidateSignatures(tx.Address, fragments.ToArray(), tx.Bundle); if (!valid) { throw new ArgumentException(Constants.INVALID_SIGNATURES_ERROR); } } // sum of all transaction must be 0 if (totalSum != 0) { throw new ArgumentException(Constants.INVALID_BUNDLE_SUM_ERROR); } sbyte[] bundleHashTrits = new sbyte[Sponge.HASH_LENGTH]; curl.Squeeze(bundleHashTrits, 0, Sponge.HASH_LENGTH); string bundleHash = Converter.ToTrytes(bundleHashTrits); if (!bundleHash.Equals(bundle.Transactions[0].Bundle, StringComparison.Ordinal)) { throw new ArgumentException(Constants.INVALID_BUNDLE_HASH_ERROR); } return(true); }
/// <summary> /// This function returns the bundle which is associated with a transaction. Input can by any type of transaction (tail and non-tail). /// If there are conflicting bundles (because of a replay for example) it will return multiple bundles. /// It also does important validation checking (signatures, sum, order) to ensure that the correct bundle is returned. /// </summary> /// <param name="transaction">the transaction encoded in trytes</param> /// <returns>an array of bundle, if there are multiple arrays it means that there are conflicting bundles.</returns> public Bundle GetBundle(string transaction) { Bundle bundle = TraverseBundle(transaction, null, new Bundle()); if (bundle == null) { throw new ArgumentException("Unknown Bundle"); } long totalSum = 0; string bundleHash = bundle.Transactions[0].Bundle; curl.Reset(); List <Signature> signaturesToValidate = new List <Signature>(); for (int index = 0; index < bundle.Transactions.Count; index++) { Transaction bundleTransaction = bundle.Transactions[index]; long bundleValue = long.Parse(bundleTransaction.Value); totalSum += bundleValue; if (long.Parse(bundleTransaction.CurrentIndex) != index) { throw new InvalidBundleException("The index of the bundle " + bundleTransaction.CurrentIndex + " did not match the expected index " + index); } // Get the transaction trytes string thisTxTrytes = bundleTransaction.ToTransactionTrytes().Substring(2187, 162); // Absorb bundle hash + value + timestamp + lastIndex + currentIndex trytes. curl.Absorb(Converter.ToTrits(thisTxTrytes)); // Check if input transaction if (bundleValue < 0) { string address = bundleTransaction.Address; Signature sig = new Signature(); sig.Address = address; sig.SignatureFragments.Add(bundleTransaction.SignatureFragment); // Find the subsequent txs with the remaining signature fragment for (int i = index; i < bundle.Length - 1; i++) { var newBundleTx = bundle[i + 1]; // Check if new tx is part of the signature fragment if (newBundleTx.Address == address && long.Parse(newBundleTx.Value) == 0) { sig.SignatureFragments.Add(newBundleTx.SignatureFragment); } } signaturesToValidate.Add(sig); } } // Check for total sum, if not equal 0 return error if (totalSum != 0) { throw new InvalidBundleException("Invalid Bundle Sum"); } int[] bundleFromTrxs = new int[243]; curl.Squeeze(bundleFromTrxs); string bundleFromTxString = Converter.ToTrytes(bundleFromTrxs); // Check if bundle hash is the same as returned by tx object if (!bundleFromTxString.Equals(bundleHash)) { throw new InvalidBundleException("Invalid Bundle Hash"); } // Last tx in the bundle should have currentIndex === lastIndex bundle.Length = bundle.Transactions.Count; if ( !bundle.Transactions[bundle.Length - 1].CurrentIndex.Equals( bundle.Transactions[bundle.Length - 1].LastIndex)) { throw new InvalidBundleException("Invalid Bundle"); } // Validate the signatures foreach (Signature aSignaturesToValidate in signaturesToValidate) { String[] signatureFragments = aSignaturesToValidate.SignatureFragments.ToArray(); string address = aSignaturesToValidate.Address; bool isValidSignature = new Signing().ValidateSignatures(address, signatureFragments, bundleHash); if (!isValidSignature) { throw new InvalidSignatureException(); } } return(bundle); }