MetacoSA / NBitcoin

Comprehensive Bitcoin library for the .NET framework.
MIT License
1.85k stars 839 forks source link

How to use NBitcoin to verify historical tx transactions data? #1206

Closed toolgood closed 3 months ago

toolgood commented 3 months ago

How to use NBitcoin to verify historical tx transactions data?

data sources: https://api.blockchain.info/haskoin-store/btc/transaction/d518d87ad11da17325e3489ea15e1c5c1e1541fd078aa239158dff028e62cdeb

{
  "txid": "d518d87ad11da17325e3489ea15e1c5c1e1541fd078aa239158dff028e62cdeb",
  "size": 347,
  "version": 1,
  "locktime": 0,
  "fee": 0,
  "inputs": [
    {
      "coinbase": false,
      "txid": "0e4826839dcde262c8907aa81059bc29f3835cfc89d95aa4b6dc4992070f05b6",
      "output": 0,
      "sigscript": "483045022100c3199fde84e8fcd18e2858530bf8024ee6e967763958451d34d7d2556ce5bfbf02204d328dd9a161448063b7bc5bdcf456a0f490afdcf478fdace8c2d7a66325453d01",
      "sequence": 4294967295,
      "pkscript": "4104a39b9e4fbd213ef24bb9be69de4a118dd0644082e47c01fd9159d38637b83fbcdc115a5d6e970586a012d1cfe3e3a8b1a3d04e763bdc5a071c0e827c0bd834a5ac",
      "value": 3010266,
      "address": "1VayNert3x1KzbpzMGt2qdqrAThiRovi8",
      "witness": []
    },
    {
      "coinbase": false,
      "txid": "7ff27c4525241adf1d312bad771c1faa15356702e16a250f357c8a6abcd30ede",
      "output": 0,
      "sigscript": "473044022012dc1a50880752427e9baa9da02c9441e56abd1ae74dbf24b6bc7380eb628a78022024563f816320f84bf78e899660415e56ebb1d7fe5c28d2bdeb02938704911f1401",
      "sequence": 4294967295,
      "pkscript": "4104a39b9e4fbd213ef24bb9be69de4a118dd0644082e47c01fd9159d38637b83fbcdc115a5d6e970586a012d1cfe3e3a8b1a3d04e763bdc5a071c0e827c0bd834a5ac",
      "value": 3262028,
      "address": "1VayNert3x1KzbpzMGt2qdqrAThiRovi8",
      "witness": []
    }
  ],
  "outputs": [
    {
      "address": "1VayNert3x1KzbpzMGt2qdqrAThiRovi8",
      "pkscript": "4104a39b9e4fbd213ef24bb9be69de4a118dd0644082e47c01fd9159d38637b83fbcdc115a5d6e970586a012d1cfe3e3a8b1a3d04e763bdc5a071c0e827c0bd834a5ac",
      "value": 1272294,
      "spent": true,
      "spender": {
        "txid": "33f5dbea73d3da1ebc91b7fc7e13c50f88841caccd403a6a92326d07c1918c2d",
        "input": 1
      }
    },
    {
      "address": "137eJq1eFi9JgiGLtotG3eqR8P9c3HzGp4",
      "pkscript": "76a9141731e3d4e096546c8269c5d75c8b4357c8abfd1a88ac",
      "value": 5000000,
      "spent": true,
      "spender": {
        "txid": "a16129816e54401aa0e7ebdc0abb4c3d348dbc1ed0ca3eb7cab19bed4611eb2d",
        "input": 21
      }
    }
  ],
  "block": {
    "height": 168004,
    "position": 61
  },
  "deleted": false,
  "time": 1329948589,
  "rbf": false,
  "weight": 1388
}

The partial completion code is as follows

using NBitcoin;
using NBitcoin.DataEncoders;
using Newtonsoft.Json;
using System.Net;

namespace ConsoleApp10
{
    public class Raw2Tx
    {
        public string txid { get; set; }
        public uint version { get; set; }
        public int locktime { get; set; }
        public List<Raw2TxInput> inputs { get; set; }
        public List<Raw2TxOutput> outputs { get; set; }
    }
    public class Raw2TxInput
    {
        public string txid { get; set; }
        public int output { get; set; }
        public string sigscript { get; set; }
        public uint sequence { get; set; }
        public string pkscript { get; set; }
        public long value { get; set; }
        public string address { get; set; }
        public List<string> witness { get; set; }

    }
    public class Raw2TxOutput
    {
        public string address { get; set; }
        public string pkscript { get; set; }
        public long value { get; set; }
        public bool spent { get; set; }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            var tx_hash = "d518d87ad11da17325e3489ea15e1c5c1e1541fd078aa239158dff028e62cdeb";
            WebClient webClient = new WebClient();
            webClient.Headers.Add(HttpRequestHeader.Accept, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
            webClient.Headers.Add(HttpRequestHeader.AcceptLanguage, "zh-CN,zh;q=0.8");
            webClient.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0");

            var url = $"https://api.blockchain.info/haskoin-store/btc/transaction/{tx_hash}";
            var json = webClient.DownloadString(url);
            var tx_raw = JsonConvert.DeserializeObject<Raw2Tx>(json);

            Transaction tx = Network.Main.CreateTransaction();
            tx.Version = tx_raw.version;
            tx.LockTime = tx_raw.locktime;

            for (int i = 0; i < tx_raw.inputs.Count; i++) {
                WitScript witScript = null;
                if (tx_raw.inputs[i].witness.Count > 0) {
                    witScript = new WitScript(string.Join('\t', tx_raw.inputs[i].witness));
                }
                TxIn txIn = tx.Inputs.CreateNewTxIn(new OutPoint(uint256.Parse(tx_raw.inputs[i].txid), i), new Script(tx_raw.inputs[i].sigscript), witScript, tx_raw.inputs[i].sequence);
                tx.Inputs.Add(txIn);
            }
            for (int i = 0; i < tx_raw.outputs.Count; i++) {
                TxOut txOut = tx.Outputs.CreateNewTxOut(new Money(tx_raw.outputs[i].value), new Script(tx_raw.outputs[i].pkscript));
                tx.Outputs.Add(txOut);
            }
            for (int i = 0; i < tx_raw.inputs.Count; i++) {
                var scriptCode = new Script(tx_raw.inputs[i].pkscript);
                var input = new TxOut(new Money(tx_raw.inputs[i].value), new Script(tx_raw.inputs[i].pkscript));

                var message = tx.GetSignatureHash(scriptCode, i, SigHash.All, input, HashVersion.Original);
                if (VerifyMessage(i, message, tx_raw)) {
                    Console.WriteLine("message is " + message);
                    continue;
                }
                var message2 = tx.GetSignatureHash(scriptCode, i, SigHash.All, input, HashVersion.WitnessV0);
                if (VerifyMessage(i, message2, tx_raw)) {
                    Console.WriteLine("message is " + message2);
                } else {
                    Console.WriteLine("message is error ");
                }
            }
        }

        static bool VerifyMessage(int i, uint256 message, Raw2Tx tx)
        {
            byte[] bs = Encoders.Hex.DecodeData(tx.inputs[i].sigscript);
            BinaryReader binaryReader = new BinaryReader(new MemoryStream(bs));//https://raghavsood.com/blog/2018/06/10/bitcoin-signature-types-sighash
            var b1 = binaryReader.ReadByte(); // DER length
            var b2 = binaryReader.ReadByte();//30 # DER Sequence tag
            var b3 = binaryReader.ReadByte();//44 # Sequence length 0x44 (68) bytes
            var b4 = binaryReader.ReadByte();//02 # Integer element
            var len = (int)binaryReader.ReadByte();//20 # Element length 0x20 (32) bytes
            var R = binaryReader.ReadBytes(len); //# ECDSA r value
            var b5 = binaryReader.ReadByte();// 02 # Integer element
            len = (int)binaryReader.ReadByte(); //20 # Element length 0x20 (32) bytes
            var S = binaryReader.ReadBytes(len); //# ECDSA s value
            var b6 = binaryReader.ReadByte();  // # SIGHASH flag

            if (binaryReader.BaseStream.Position == bs.Length) {
                // P2PK https://bitcoin.stackexchange.com/questions/73758/what-are-the-standard-formats-of-transaction-outputs
                // PUSH (1 byte) + <compressed/uncompressed_pk> (33/65 bytes) + OP_CHECKSIG (1 byte)
                byte[] bs2 = Encoders.Hex.DecodeData(tx.inputs[i].pkscript);
                binaryReader = new BinaryReader(new MemoryStream(bs2));
            }
            var b7 = binaryReader.ReadByte();
            var type = binaryReader.ReadByte();
            byte[] X, Y;
            if (type == 4) {
                X = binaryReader.ReadBytes(32);
                Y = binaryReader.ReadBytes(32);
            } else if (type == 2) { // Even y-coordinate
                X = binaryReader.ReadBytes(32);
            } else if (type == 3) {// Odd y-coordinate
                X = binaryReader.ReadBytes(32);
            }
            // TODO: 

            return false;
        }
    }
}

Currently, there is still a lack of verification code. Could you please help me write the remaining code.