/// <summary>
        /// Compute group ID for a group of unsigned transactions
        /// </summary>
        /// <param name="txns">array of transactions</param>
        /// <returns>Digest</returns>
        public static Digest ComputeGroupID(params Transaction[] txns)
        {
            if (txns != null && txns.Length > 0)
            {
                if (txns.Length > MAX_TX_GROUP_SIZE)
                {
                    throw new ArgumentException("max group size is " + MAX_TX_GROUP_SIZE);
                }

                Digest[] txIDs = new Digest[txns.Length];
                for (int i = 0; i < txns.Length; ++i)
                {
                    txIDs[i] = txns[i].RawTxID();
                }


                TxGroup txgroup = new TxGroup(txIDs);
                try
                {
                    byte[] gid = Digester.Digest(txgroup.BytesToSign());
                    return(new Digest(gid));
                }
                catch (Exception e)
                {
                    throw new ArgumentException("tx computation failed", e);
                }
            }
            else
            {
                throw new ArgumentException("empty transaction list");
            }
        }
        /// <summary>
        /// check if the address is valid
        /// </summary>
        /// <param name="encodedAddress">Address</param>
        /// <returns>valid or not</returns>
        public static bool IsValid(string encodedAddress)
        {
            // interpret as base32
            var checksumAddr = Base32.DecodeFromString(encodedAddress).ToList();

            if (checksumAddr.Count != LEN_BYTES + CHECKSUM_LEN_BYTES)
            {
                return(false);
            }
            // split into checksum

            byte[] checksum = checksumAddr.GetRange(LEN_BYTES, checksumAddr.Count - LEN_BYTES).ToArray();
            byte[] addr     = checksumAddr.GetRange(0, LEN_BYTES).ToArray();

            // compute expected checksum
            var hashedAddr = Digester.Digest(addr).ToList();

            byte[] expectedChecksum = hashedAddr.GetRange(LEN_BYTES - CHECKSUM_LEN_BYTES,
                                                          hashedAddr.Count - LEN_BYTES + CHECKSUM_LEN_BYTES).ToArray();

            // compare
            if (Enumerable.SequenceEqual(checksum, expectedChecksum))
            {
                return(true);
            }

            else
            {
                return(false);
            }
        }
 // returns a word corresponding to the 11 bit checksum of the data
 internal static string Checksum(byte[] data)
 {
     //CryptoProvider.setupIfNeeded();
     //MessageDigest digest = MessageDigest.getInstance(CHECKSUM_ALG);
     //digest.update(Arrays.copyOf(data, data.length));
     //byte[] d = digest.digest();
     byte[] d = Digester.Digest(data);
     // optimize for CHECKSUM_LEN_WORDS = 1
     //d = Arrays.copyOfRange(d, 0, 2);
     return(ApplyWord(ToUintNArray(new byte[] { d[0], d[1] })[0]));
 }
        /// <summary>
        /// building an address object helps us generate string representations
        /// </summary>
        /// <returns>the address</returns>
        public Address ToAddress()
        {
            List <byte> hashable = new List <byte>(PREFIX)
            {
                Convert.ToByte(this.version),
                Convert.ToByte(this.threshold)
            };

            foreach (var key in publicKeys)
            {
                hashable.AddRange(key.GetEncoded());
            }

            return(new Address(Digester.Digest(hashable.ToArray())));
        }
        /// <summary>
        /// EncodeAsString converts the address to a human-readable representation, with
        /// a 4-byte checksum appended at the end, using SHA256. Note that string representations
        /// of addresses generated by different SDKs may not be compatible.
        /// </summary>
        /// <returns>the encoded address string</returns>
        public string EncodeAsString()
        {
            // compute sha512/256 checksum, and take the last 4 bytes as the checksum
            var checksum = Digester.Digest(Bytes).ToList().GetRange(LEN_BYTES - CHECKSUM_LEN_BYTES, CHECKSUM_LEN_BYTES);

            // concat the hashed address and the bytes
            var    checksumAddress = Enumerable.Concat(Bytes, checksum);
            string res             = Base32.EncodeToString(checksumAddress.ToArray(), false);

            if (res.Length != EXPECTED_STR_ENCODED_LEN)
            {
                throw new ArgumentException("unexpected address length " + res.Length);
            }
            return(res);
        }