// $Id: bitcoin-raw.cs $
// $Date: 2016-04-01 09:47Z $
// $Revision: 1.0.0 $
// $Author: dai $

// *************************** COPYRIGHT NOTICE ******************************
// This code is copyright (C) 2016 David Ireland, DI Management Services Pty 
// Ltd, Australia <http://www.di-mgt.com.au>.
// Provided "as is" with no warranties. Use at your own risk. 
// It is not to be altered or distributed, except as part of an application.
// You are free to use it in any application, provided this copyright notice
// is left unchanged.
// ************************ END OF COPYRIGHT NOTICE **************************

// This module uses functions from CryptoSys (tm) PKI Pro available from
// <http://www.cryptosys.net/pki/>.
// Add a reference to `diCrSysPKINet.dll` and use "using CryptoSysPKI;".


using System;
using System.Text;
using CryptoSysPKI;


namespace bitcoin_raw
{
	class Program
	{
		static void Main(string[] args)
		{
			Console.WriteLine("PKI Version=" + General.Version());

			// READ IN PRIVATE KEY IN (HEX,CURVENAME) FORMAT
			string hexKey = "0ecd20654c2e2be708495853e8da35c664247040c00bd10b9b13e5e86e6a808d";
			Ecc.CurveName curveName = Ecc.CurveName.Secp256k1;
			Console.WriteLine("KEYHEX: " + hexKey);
			Console.WriteLine("CURVE:  " + curveName.ToString());
			string internalKey = Ecc.ReadKeyByCurve(hexKey, curveName);
			// Derive public key in hex form from given private key and check against known value
			string s = Ecc.QueryKey(internalKey, "publicKey");
			Console.WriteLine("Ecc.QueryKey('publicKey')=\n" + s);

			// READ IN THE PUBLIC KEY
			string pubkeyhex =
				"042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09d" +
				"b988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9";
			string internalPubKey = Ecc.ReadKeyByCurve(pubkeyhex, curveName);
			Console.WriteLine("keyBits=" + Ecc.QueryKey(internalPubKey, "keyBits"));
			Console.WriteLine("isPrivate=" + Ecc.QueryKey(internalPubKey, "isPrivate"));

			// COMPUTE THE HASH160 DIGEST OF THE PUBLIC KEY 
			s = Hash.HexFromHex(pubkeyhex, HashAlgorithm.Bitcoin160);
			Console.WriteLine("Bitcoin160('publicKey')=" + s);


			// THE RAW INPUT DATA
			string datahex = 
				"0100000001be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d539600" + 
				"0000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88acffffffff0123ce0100" + 
				"000000001976a9142bc89c2702e0e618db7d59eb5ce2f0f147b4075488ac0000000001000000";

			byte[] data = Cnv.FromHex(datahex);
			Console.WriteLine("RAW_TX:\n" + Cnv.ToHex(data));

			// COMPUTE THE DOUBLE SHA-256 DIGEST OF THE RAW DATA
			byte[] digest = Hash.Double(data, HashAlgorithm.Sha256);
			Console.WriteLine("SHA256(SHA256(RAW_TX)):\n" + Cnv.ToHex(digest));
			Console.WriteLine("Same hash value but reversed:\n" + Cnv.ToHex(Cnv.ReverseBytes(digest)));


			string sigweb =
			   "3045022100da43201760bda697222002f56266bf65023fef2094519e13077f777baed55" +
			   "3b102205ce35d05eabda58cd50a67977a65706347cc25ef43153e309ff210a134722e9e";
			// VERIFY THE SIGNATURE PUBLISHED ON THE WEB
			int r1 = Sig.VerifyDigest(sigweb, digest, internalPubKey, SigAlgorithm.Ecdsa_Sha256);
			Console.WriteLine("Sig.VerifyDigest returns " + r1 + " (expected 0)");

			// Sign using ECDSA deterministic method of RFC6979
			// -- NOTE this will not be the same as the published signature (which used a random k we cannot reproduce) 
			// but the deterministic method should always produce the same signature value:
			// 30450220587ce0cf0252e2db3a7c3c91b355aa8f3385e128227cd8727c5f7777877ad772022100edc508b7c14891ed15ab38c687019d7ebaf5c12908cf21a83e8ae57e8c47e95c
			string sigdet = Sig.SignDigest(digest, internalKey, "", SigAlgorithm.Ecdsa_Sha256,
				Sig.SigOptions.UseDeterministic | Sig.SigOptions.Asn1DERStructure, Sig.Encoding.Base16);
			Console.WriteLine("SIG (DET): " + sigdet);

			// VERIFY THE NEW SIGNATURE
			int r2 = Sig.VerifyDigest(sigdet, digest, internalPubKey, SigAlgorithm.Ecdsa_Sha256);
			Console.WriteLine("Sig.VerifyDigest(sigdet) returns " + r2 + " (expected 0)");
		}
	}
}