public void ConvertPubKeyTest()
        {
            byte[] input = Encoding.ASCII.GetBytes("abc");

            List <PubKey> keys = BlockChainDataConversions.BytesToPubKeys(input);

            byte[] converted = BlockChainDataConversions.PubKeysToBytes(keys);

            Assert.Equal(converted[0], 0x61);
            Assert.Equal(converted[1], 0x62);
            Assert.Equal(converted[2], 0x63);
        }
        public void ParseTransaction(Transaction tx, Network network)
        {
            var markerIndex = GetMarkerIndex(tx);

            if (markerIndex == -1)
            {
                throw new Exception("Missing Redstone registration marker from first transaction output");
            }

            // Peek at first non-nulldata address to get the length information,
            // this indicates how many outputs have been used for encoding, and
            // by extension indicates if there will be a change address output

            PubKey[] tempPubKeyArray = tx.Outputs[markerIndex + 1].ScriptPubKey.GetDestinationPublicKeys(network);

            if (tempPubKeyArray.Length > 1)
            {
                // This can't have been generated by a server registration, we don't use
                // multiple signatures for the registration transaction outputs by design
                throw new Exception("Registration transaction output has too many PubKeys");
            }

            byte[] secondOutputData = BlockChainDataConversions.PubKeyToBytes(tempPubKeyArray[0]);

            var protocolVersion = (int)secondOutputData[0];

            var headerLength = (secondOutputData[2] << 8) + secondOutputData[1];

            // 64 = number of bytes we can store per output
            int numPubKeys = headerLength / 64;

            // Is there a partially 'full' PubKey holding the remainder of the bytes?
            if (headerLength % 64 != 0)
            {
                numPubKeys++;
            }

            if (tx.Outputs.Count < numPubKeys + 1)
            {
                throw new Exception("Too few transaction outputs, registration transaction incomplete");
            }

            PubKey[] tempPK;
            var      pubKeyList = new List <PubKey>();

            for (int i = markerIndex + 1; i < numPubKeys + markerIndex + 1; i++)
            {
                tempPK = tx.Outputs[i].ScriptPubKey.GetDestinationPublicKeys(network);

                if (tempPK.Length > 1)
                {
                    // This can't have been generated by a server registration, we don't use
                    // multiple signatures for the registration transaction outputs by design
                    throw new Exception("Registration transaction output has too many PubKeys");
                }

                pubKeyList.Add(tempPK[0]);
            }

            byte[] bitstream = BlockChainDataConversions.PubKeysToBytes(pubKeyList);

            // Need to consume X bytes at a time off the bitstream and convert them to various
            // data types, then set member variables to the retrieved values.

            // Skip over protocol version and header length bytes
            int position = 3;

            this.ProtocolVersion = protocolVersion;

            byte[] collateralPubKeyHashTemp = GetSubArray(bitstream, position, 20);
            this.CollateralPubKeyHash = new KeyId(collateralPubKeyHashTemp);
            position += 20;

            byte[] rewaredPubKeyHashTemp = GetSubArray(bitstream, position, 20);
            this.RewardPubKeyHash = new KeyId(rewaredPubKeyHashTemp);
            position += 20;

            // Either a valid IPv4 address, or all zero bytes
            bool allZeroes = true;

            byte[] ipv4temp = GetSubArray(bitstream, position, 4);

            for (int i = 0; i < ipv4temp.Length; i++)
            {
                if (ipv4temp[i] != 0)
                {
                    allZeroes = false;
                }
            }

            if (!allZeroes)
            {
                this.Ipv4Addr = new IPAddress(ipv4temp);
            }
            else
            {
                this.Ipv4Addr = IPAddress.None;
            }

            position += 4;

            // Either a valid IPv6 address, or all zero bytes
            allZeroes = true;
            byte[] ipv6temp = GetSubArray(bitstream, position, 16);

            for (int i = 0; i < ipv6temp.Length; i++)
            {
                if (ipv6temp[i] != 0)
                {
                    allZeroes = false;
                }
            }

            if (!allZeroes)
            {
                this.Ipv6Addr = new IPAddress(ipv6temp);
            }
            else
            {
                this.Ipv6Addr = IPAddress.IPv6None;
            }

            position += 16;

            // Either a valid onion address, or all zero bytes
            allZeroes = true;
            byte[] onionTemp = GetSubArray(bitstream, position, 16);

            for (int i = 0; i < onionTemp.Length; i++)
            {
                if (onionTemp[i] != 0)
                {
                    allZeroes = false;
                }
            }

            if (!allZeroes)
            {
                this.OnionAddress = Encoding.ASCII.GetString(onionTemp);
            }
            else
            {
                this.OnionAddress = null;
            }

            position += 16;

            var temp = GetSubArray(bitstream, position, 2);

            this.Port = (temp[1] << 8) + temp[0];
            position += 2;

            temp = GetSubArray(bitstream, position, 2);
            var servicEndpointLength = (temp[1] << 8) + temp[0];

            position += 4;

            var serviceEndpointTemp = GetSubArray(bitstream, position, servicEndpointLength);

            this.ServiceEndpoint = new Uri(Encoding.ASCII.GetString(serviceEndpointTemp));
            position            += servicEndpointLength;

            temp = GetSubArray(bitstream, position, 2);
            var ecdsaLength = (temp[1] << 8) + temp[0];

            position += 4;

            this.Signature = GetSubArray(bitstream, position, ecdsaLength);
            position      += ecdsaLength;

            temp = GetSubArray(bitstream, position, 2);
            var ecdsaPubKeyLength = (temp[1] << 8) + temp[0];

            position += 2;

            this.PubKey = new PubKey(GetSubArray(bitstream, position, ecdsaPubKeyLength));
            position   += ecdsaPubKeyLength;

            // TODO: Validate signatures
        }