Exemplo n.º 1
0
 public void CashAddressChecksumValidationTest()
 {
     foreach (var(address, decoded) in _cashAddressTestCases)
     {
         // ensure correct checksum validates
         Assert.IsTrue(CashAddress.ValidateChecksum(address, decoded));
     }
 }
Exemplo n.º 2
0
 public void CashAddressEncodeTest()
 {
     foreach (var testCase in _cashAddressTestCases)
     {
         // ensure the prefix is valid
         Assert.IsTrue(Enum.IsDefined(typeof(AddressPrefix), testCase.Value.Prefix));
         // create the cash address
         var encoded = CashAddress.EncodeCashAddress(Enum.Parse <AddressPrefix>(testCase.Value.Prefix), testCase.Value.Type, testCase.Value.Hash);
         // ensure cash address output is correct
         Assert.AreEqual(encoded, testCase.Key);
     }
 }
Exemplo n.º 3
0
 public void CashAddressDecodeTest()
 {
     foreach (var testCase in _cashAddressTestCases)
     {
         // decode the cash address
         var decoded = CashAddress.DecodeCashAddress(testCase.Key);
         // ensure all bytes of the hash output are correct
         CollectionAssert.AreEqual(decoded.Hash, testCase.Value.Hash);
         // ensure type output is correct
         Assert.AreEqual(decoded.Type, testCase.Value.Type);
         // ensure prefix output is correct
         Assert.AreEqual(decoded.Prefix, testCase.Value.Prefix);
     }
 }
Exemplo n.º 4
0
        /// <summary>
        ///     Handles json messages:
        ///         {"op": "block"}
        ///         {"op": "transaction"}
        ///         {"op": "address", "address": "[CASH_ADDRESS]"}
        ///         {"op": "opreturn", "prefix": "[PREFIX(HEX)]"}
        ///         {"op": "rm_block"}
        ///         {"op": "rm_transaction"}
        ///         {"op": "rm_address", "address": "[CASH_ADDRESS]"}
        ///         {"op": "rm_opreturn", "prefix": "[PREFIX(HEX)]"}
        ///     - Validates json
        ///     - Validates parameters
        ///     - Sends response (error or ok)
        ///     - Adds valid subscriptions to subscription handler
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="message"></param>
        /// <param name="subscriptionHandler"></param>
        public static void HandleMessage(IWebsocketConnection socket, string message, SubscriptionHandler subscriptionHandler)
        {
            message = message.Trim();

            // check if the message is valid json
            if ((!message.StartsWith("{") || !message.EndsWith("}")) &&
                (!message.StartsWith("[") || !message.EndsWith("]")))
            {
                return;
            }

            JToken jToken;

            try
            {
                jToken = JToken.Parse(message);
            }
            catch (JsonReaderException jex)
            {
                socket.Send("{ \"op\": \"error\", \"error\": \"Unable to parse JSON request. " + JsonConvert.ToString(jex.Message) + "\" }");
                return;
            }
            catch (Exception ex)
            {
                socket.Send("{ \"op\": \"error\", \"error\": \"Exception while parsing JSON request. " + JsonConvert.ToString(ex.Message) + "\" }");
                return;
            }

            if (jToken.Type != JTokenType.Object)
            {
                socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected Object, but encountered " + jToken.Type + "\" }");
                return;
            }

            var jObject = jToken.ToObject <JObject>();

            if (!jObject.ContainsKey("op") || jObject["op"].Type != JTokenType.String)
            {
                socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'op' parameter as string.\" }");
                return;
            }

            // block subscriptions
            if (jObject["op"].ToString().Equals("block", StringComparison.CurrentCultureIgnoreCase))
            {
                subscriptionHandler.AddSubscription(socket, new BlockSubscription());
                socket.Send("{ \"op\": \"block\", \"result\": \"ok\" }");
            }
            // block un-subscribe
            else if (jObject["op"].ToString().Equals("rm_block", StringComparison.CurrentCultureIgnoreCase))
            {
                socket.Send(subscriptionHandler.RemoveSubscription(socket, new BlockSubscription())
                    ? "{ \"op\": \"rm_block\", \"result\": \"ok\" }"
                    : "{ \"op\": \"rm_block\", \"result\": \"failed\" }");
            }

            // transaction subscriptions
            else if (jObject["op"].ToString().Equals("transaction", StringComparison.CurrentCultureIgnoreCase))
            {
                subscriptionHandler.AddSubscription(socket, new TransactionSubscription());
                socket.Send("{ \"op\": \"transaction\", \"result\": \"ok\" }");
            }
            // transaction un-subscribe
            else if (jObject["op"].ToString().Equals("rm_transaction", StringComparison.CurrentCultureIgnoreCase))
            {
                socket.Send(subscriptionHandler.RemoveSubscription(socket, new TransactionSubscription())
                    ? "{ \"op\": \"rm_transaction\", \"result\": \"ok\" }"
                    : "{ \"op\": \"rm_transaction\", \"result\": \"failed\" }");
            }

            // address subscriptions
            else if (jObject["op"].ToString().Equals("address", StringComparison.CurrentCultureIgnoreCase) ||
                     jObject["op"].ToString().Equals("rm_address", StringComparison.CurrentCultureIgnoreCase))
            {
                if (!jObject.ContainsKey("address") || jObject["address"].Type != JTokenType.String)
                {
                    socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'address' parameter as string.\" }");
                    return;
                }

                // attempt to decode cash address
                try
                {
                    var decoded = CashAddress.DecodeCashAddress(jObject["address"].ToString());

                    if (jObject["op"].ToString().Equals("address", StringComparison.CurrentCultureIgnoreCase))
                    {
                        subscriptionHandler.AddSubscription(socket, new AddressSubscription(decoded));
                        socket.Send("{ \"op\": \"address\", \"result\": \"ok\" }");
                    }

                    else if (jObject["op"].ToString().Equals("rm_address", StringComparison.CurrentCultureIgnoreCase))
                    {
                        socket.Send(!subscriptionHandler.RemoveSubscription(socket, new AddressSubscription(decoded))
                            ? "{ \"op\": \"rm_address\", \"result\": \"failed\" }"
                            : "{ \"op\": \"rm_address\", \"result\": \"ok\" }");
                    }
                }
                catch (CashAddress.CashAddressException e)
                {
                    socket.Send("{ \"op\": \"error\", \"error\": \"" + JsonConvert.ToString(e.Message) + " ... " + JsonConvert.ToString(e.InnerException.Message) + "\" }");
                }
            }

            // op return subscriptions
            else if (jObject["op"].ToString().Equals("opreturn", StringComparison.CurrentCultureIgnoreCase) ||
                     jObject["op"].ToString().Equals("rm_opreturn", StringComparison.CurrentCultureIgnoreCase))
            {
                if (!jObject.ContainsKey("prefix") || jObject["prefix"].Type != JTokenType.String)
                {
                    socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'prefix' parameter as string.\" }");
                    return;
                }
                if (!IsValidHexString(jObject["prefix"].ToString()))
                {
                    socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'prefix' parameter to be valid hex.\" }");
                    return;
                }
                if (jObject["prefix"].ToString().Length > 32)
                {
                    socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected 'prefix' parameter to be less than 32 hex characters long.\" }");
                    return;
                }

                var prefix = ByteHexConverter.StringToByteArray(jObject["prefix"].ToString());

                if (jObject["op"].ToString().Equals("opreturn", StringComparison.CurrentCultureIgnoreCase))
                {
                    subscriptionHandler.AddSubscription(socket, new OpReturnSubscription(prefix));
                    socket.Send("{ \"op\": \"opreturn\", \"result\": \"ok\" }");
                }
                else if (subscriptionHandler.RemoveSubscription(socket,
                                                                new OpReturnSubscription(prefix)))
                {
                    socket.Send(!subscriptionHandler.RemoveSubscription(socket, new OpReturnSubscription(prefix))
                        ? "{ \"op\": \"rm_opreturn\", \"result\": \"failed\" }"
                        : "{ \"op\": \"rm_opreturn\", \"result\": \"ok\" }");
                }
            }

            else
            {
                // unrecognized op command
                socket.Send("{ \"op\": \"error\", \"error\": \"Error while parsing JSON request. Expected valid 'op' parameter.\" }");
            }
        }
Exemplo n.º 5
0
        public static void Main(string[] args)
        {
            Console.WriteLine("Creating output script for 'bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a'...");
            // input cash address and produce an output script
            var decoded = CashAddress.DecodeCashAddress("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a");

            // so far, cash addresses have two defined types
            // let's print the readable output script for the address
            switch (decoded.Type)
            {
            // our demo happens to be a P2PKH cash address...
            case ScriptType.P2PKH:
                // use ByteHexConverter to convert raw byte data for output script to readable hex
                Console.WriteLine("Output script: " +
                                  "OP_DUP OP_HASH160 " + ByteHexConverter.ByteArrayToHex(decoded.Hash) + " OP_EQUALVERIFY OP_CHECKSIG");
                break;

            // if it was a P2SH cash address...
            case ScriptType.P2SH:
                // use ByteHexConverter to convert raw byte data for output script to readable hex
                Console.WriteLine("Output script: " +
                                  "OP_HASH160 " + ByteHexConverter.ByteArrayToHex(decoded.Hash) + " OP_CHECKSIG");
                break;

            // whoa... another type?
            default:
                Console.WriteLine("This shouldn't happen! That's an unknown cash address type!");
                break;
            }

            // let's use script builder now
            var outputScript = ScriptBuilder.CreateOutputScript(decoded.Type, decoded.Hash);

            // same thing if we go straight from a Cash Address
            outputScript = ScriptBuilder.CreateOutputScript("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a");
            Console.WriteLine("Output script from ScriptBuilder: " + outputScript);
            Console.WriteLine("Output script RAW from ScriptBuilder: " + ByteHexConverter.ByteArrayToHex(outputScript.ScriptBytes));

            // What about if we want to create an OP_RETURN output?
            // ASCII encode "My Bitcoin OP_RETURN!" and create an output script
            var opReturn = ScriptBuilder.CreateOpReturn(Encoding.ASCII.GetBytes("My Bitcoin OP_RETURN!"));

            Console.WriteLine("OP_RETURN script from ScriptBuilder: " + opReturn);
            Console.WriteLine("OP_RETURN script RAW from ScriptBuilder: " + ByteHexConverter.ByteArrayToHex(opReturn.ScriptBytes));


            // encode a hash160 from an output script as a cash address (demo script is P2PKH)
            Console.WriteLine("Encoding output script 'OP_DUP OP_HASH160 76a04053bda0a88bda5177b86a15c3b29f559873 OP_EQUALVERIFY OP_CHECKSIG'...");
            // use ByteHexConverter to convert the readable hex to raw byte data (as it would actually be encoded in an output script)
            var encoded = CashAddress.EncodeCashAddress(AddressPrefix.bitcoincash, ScriptType.P2PKH,
                                                        ByteHexConverter.StringToByteArray("76a04053bda0a88bda5177b86a15c3b29f559873"));

            Console.WriteLine("Cash Address: " + encoded);


            // let's try decoding a raw transaction!
            var txHex =
                "020000000113b15104613103365466d9c1773a2c60c3dec7ab6ea41f7f2824f6b00556bd98370000006b483045022100bda8b53dcffbcbf3c005b7c55a923cd04eb3d3abd7632dd260f97d15cc2982ed02202dc15d4a9ad826f4b3a0781693050fe8c1cdeb919903ba11385f0b5e83c1ea5641210384dd3ad997f2e10980e755236b474f986c519599946027876cdeb4eb5a30a09fffffffff0110270000000000001976a91476a04053bda0a88bda5177b86a15c3b29f55987388ac00000000";

            var tx = new Transaction(ByteHexConverter.StringToByteArray(txHex));

            Console.WriteLine("Decoded transaction. TXID: " + tx.TXIDHex + ". Inputs: " + tx.Inputs.Length + ". Outputs: " + tx.Outputs.Length + ". Output Scripts:");
            foreach (var output in tx.Outputs)
            {
                Console.WriteLine(output);
            }

            // wait
            Console.ReadKey();
        }