github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/wallet/decode.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"fmt"
     7  
     8  	"github.com/piotrnar/gocoin/lib/btc"
     9  )
    10  
    11  // hex_dump returns the hex dump with max 32 bytes per line.
    12  func hex_dump(d []byte) (s string) {
    13  	for {
    14  		le := 32
    15  		if len(d) < le {
    16  			le = len(d)
    17  		}
    18  		s += "       " + hex.EncodeToString(d[:le]) + "\n"
    19  		d = d[le:]
    20  		if len(d) == 0 {
    21  			return
    22  		}
    23  	}
    24  }
    25  
    26  func dump_raw_sigscript(d []byte) bool {
    27  	ss, er := btc.ScriptToText(d)
    28  	if er != nil {
    29  		println(er.Error())
    30  		return false
    31  	}
    32  
    33  	p2sh := len(ss) >= 2 && d[0] == 0
    34  	if p2sh {
    35  		ms, er := btc.NewMultiSigFromScript(d)
    36  		if er == nil {
    37  			fmt.Println("      Multisig script", ms.SigsNeeded, "of", len(ms.PublicKeys))
    38  			for i := range ms.PublicKeys {
    39  				fmt.Printf("       pkey%d = %s\n", i+1, hex.EncodeToString(ms.PublicKeys[i]))
    40  			}
    41  			for i := range ms.Signatures {
    42  				fmt.Printf("       R%d = %64s\n", i+1, hex.EncodeToString(ms.Signatures[i].R.Bytes()))
    43  				fmt.Printf("       S%d = %64s\n", i+1, hex.EncodeToString(ms.Signatures[i].S.Bytes()))
    44  				fmt.Printf("       HashType%d = %02x\n", i+1, ms.Signatures[i].HashType)
    45  			}
    46  			return len(ms.Signatures) >= int(ms.SigsNeeded)
    47  		} else {
    48  			println(er.Error())
    49  		}
    50  	}
    51  
    52  	fmt.Println("      SigScript:")
    53  	for i := range ss {
    54  		if p2sh && i == len(ss)-1 {
    55  			// Print p2sh script
    56  			d, _ = hex.DecodeString(ss[i])
    57  			s2, er := btc.ScriptToText(d)
    58  			if er != nil {
    59  				println(er.Error())
    60  				p2sh = false
    61  				fmt.Println("       ", ss[i])
    62  				continue
    63  				//return
    64  			}
    65  			fmt.Println("        P2SH spend script:")
    66  			for j := range s2 {
    67  				fmt.Println("        ", s2[j])
    68  			}
    69  		} else {
    70  			fmt.Println("       ", ss[i])
    71  		}
    72  	}
    73  	return true
    74  }
    75  
    76  func dump_sigscript(d []byte) bool {
    77  	if len(d) == 0 {
    78  		fmt.Println("       WARNING: Empty sigScript")
    79  		return false
    80  	}
    81  	rd := bytes.NewReader(d)
    82  
    83  	// ECDSA Signature
    84  	le, _ := rd.ReadByte()
    85  	if le < 0x40 {
    86  		return dump_raw_sigscript(d)
    87  	}
    88  	sd := make([]byte, le)
    89  	_, er := rd.Read(sd)
    90  	if er != nil {
    91  		return dump_raw_sigscript(d)
    92  	}
    93  	sig, er := btc.NewSignature(sd)
    94  	if er != nil {
    95  		return dump_raw_sigscript(d)
    96  	}
    97  	fmt.Printf("       R = %64s\n", hex.EncodeToString(sig.R.Bytes()))
    98  	fmt.Printf("       S = %64s\n", hex.EncodeToString(sig.S.Bytes()))
    99  	fmt.Printf("       HashType = %02x\n", sig.HashType)
   100  
   101  	// Key
   102  	le, er = rd.ReadByte()
   103  	if er != nil {
   104  		fmt.Println("       WARNING: PublicKey not present")
   105  		fmt.Print(hex_dump(d))
   106  		return false
   107  	}
   108  
   109  	sd = make([]byte, le)
   110  	_, er = rd.Read(sd)
   111  	if er != nil {
   112  		fmt.Println("       WARNING: PublicKey too short", er.Error())
   113  		fmt.Print(hex_dump(d))
   114  		return false
   115  	}
   116  
   117  	fmt.Printf("       PublicKeyType = %02x\n", sd[0])
   118  	key, er := btc.NewPublicKey(sd)
   119  	if er != nil {
   120  		fmt.Println("       WARNING: PublicKey broken", er.Error())
   121  		fmt.Print(hex_dump(d))
   122  		return false
   123  	}
   124  	fmt.Printf("       X = %64s\n", key.X.String())
   125  	if le >= 65 {
   126  		fmt.Printf("       Y = %64s\n", key.Y.String())
   127  	}
   128  
   129  	if rd.Len() != 0 {
   130  		fmt.Println("       WARNING: Extra bytes at the end of sigScript")
   131  		fmt.Print(hex_dump(d[len(d)-rd.Len():]))
   132  	}
   133  	return true
   134  }
   135  
   136  // dump_raw_tx dumps a raw transaction.
   137  func dump_raw_tx() {
   138  	tx := raw_tx_from_file(*dumptxfn)
   139  	if tx == nil {
   140  		fmt.Println("ERROR: Cannot decode the raw transaction")
   141  		return
   142  	}
   143  
   144  	var unsigned, totin, totout, noins uint64
   145  
   146  	fmt.Println("ID:", tx.Hash.String())
   147  	fmt.Println("WTxID:", tx.WTxID().String())
   148  	fmt.Println("Tx Version:", tx.Version)
   149  	if tx.SegWit != nil {
   150  		fmt.Println("Segregated Witness transaction", len(tx.SegWit))
   151  	} else {
   152  		fmt.Println("Regular (non-SegWit) transaction", len(tx.SegWit))
   153  	}
   154  	if tx.IsCoinBase() {
   155  		if len(tx.TxIn[0].ScriptSig) >= 4 && tx.TxIn[0].ScriptSig[0] == 3 {
   156  			fmt.Println("Coinbase TX from block height", uint(tx.TxIn[0].ScriptSig[1])|
   157  				uint(tx.TxIn[0].ScriptSig[2])<<8|uint(tx.TxIn[0].ScriptSig[3])<<16)
   158  		} else {
   159  			fmt.Println("Coinbase TX from an unknown block")
   160  		}
   161  		s := hex.EncodeToString(tx.TxIn[0].ScriptSig)
   162  		for len(s) > 0 {
   163  			i := len(s)
   164  			if i > 64 {
   165  				i = 64
   166  			}
   167  			fmt.Println("  ", s[:i])
   168  			s = s[i:]
   169  		}
   170  		for wia := range tx.SegWit {
   171  			for wib, ww := range tx.SegWit[wia] {
   172  				fmt.Println("  Witness", wia, wib, hex.EncodeToString(ww))
   173  			}
   174  		}
   175  		//fmt.Println()
   176  	} else {
   177  		fmt.Println("TX IN cnt:", len(tx.TxIn))
   178  		for i := range tx.TxIn {
   179  			fmt.Printf("%4d) %s sl=%d seq=%08x\n", i, tx.TxIn[i].Input.String(),
   180  				len(tx.TxIn[i].ScriptSig), tx.TxIn[i].Sequence)
   181  
   182  			if intx := tx_from_balance(btc.NewUint256(tx.TxIn[i].Input.Hash[:]), false); intx != nil {
   183  				val := intx.TxOut[tx.TxIn[i].Input.Vout].Value
   184  				totin += val
   185  				fmt.Printf("%15s BTC from address %s\n", btc.UintToBtc(val),
   186  					btc.NewAddrFromPkScript(intx.TxOut[tx.TxIn[i].Input.Vout].Pk_script, testnet))
   187  			} else {
   188  				noins++
   189  			}
   190  
   191  			if len(tx.TxIn[i].ScriptSig) > 0 {
   192  				if !dump_sigscript(tx.TxIn[i].ScriptSig) {
   193  					unsigned++
   194  				}
   195  			} else {
   196  				if tx.SegWit == nil || len(tx.SegWit[i]) < 2 {
   197  					if i < len(tx.SegWit) && len(tx.SegWit[i]) == 1 && (len(tx.SegWit[i][0])|1) == 65 {
   198  						fmt.Println("      Schnorr signature:")
   199  						fmt.Println("       ", hex.EncodeToString(tx.SegWit[i][0][:32]))
   200  						fmt.Println("       ", hex.EncodeToString(tx.SegWit[i][0][32:]))
   201  						if len(tx.SegWit[i][0]) == 65 {
   202  							fmt.Printf("        Hash Type 0x%02x\n", tx.SegWit[i][0][64])
   203  						}
   204  						goto skip_wintesses
   205  					} else {
   206  						unsigned++
   207  					}
   208  				}
   209  			}
   210  			if tx.SegWit != nil {
   211  				fmt.Println("      Witness data:")
   212  				for _, ww := range tx.SegWit[i] {
   213  					if len(ww) == 0 {
   214  						fmt.Println("       ", "OP_0")
   215  					} else {
   216  						fmt.Println("       ", hex.EncodeToString(ww))
   217  					}
   218  				}
   219  			}
   220  		skip_wintesses:
   221  		}
   222  	}
   223  	fmt.Println("TX OUT cnt:", len(tx.TxOut))
   224  	for i := range tx.TxOut {
   225  		totout += tx.TxOut[i].Value
   226  		fmt.Printf("%4d) %20s BTC ", i, btc.UintToBtc(tx.TxOut[i].Value))
   227  		addr := addr_from_pkscr(tx.TxOut[i].Pk_script)
   228  		if addr != nil {
   229  			if addr.Version == ver_script() {
   230  				fmt.Println("to scriptH", addr.String())
   231  			} else {
   232  				fmt.Println("to address", addr.String())
   233  			}
   234  		} else {
   235  			if tx.TxOut[i].Value > 0 {
   236  				fmt.Println("WARNING!!! These coins go to non-standard Pk_script:")
   237  			} else {
   238  				fmt.Println("NULL output to Pk_script:")
   239  			}
   240  			ss, er := btc.ScriptToText(tx.TxOut[i].Pk_script)
   241  			if er == nil {
   242  				for i := range ss {
   243  					fmt.Println("       ", ss[i])
   244  				}
   245  			} else {
   246  				fmt.Println(hex.EncodeToString(tx.TxOut[i].Pk_script))
   247  				fmt.Println(er.Error())
   248  			}
   249  		}
   250  	}
   251  	fmt.Println("Lock Time:", tx.Lock_time)
   252  
   253  	fmt.Println("Output volume:", btc.UintToBtc(totout), "BTC")
   254  	if noins == 0 {
   255  		fmt.Println("Input volume :", btc.UintToBtc(totin), "BTC")
   256  		fmt.Println("Transact. fee:", btc.UintToBtc(totin-totout), "BTC ->",
   257  			fmt.Sprintf("%.3f", float64(totin-totout)/float64(tx.VSize())), "SPB")
   258  	} else {
   259  		fmt.Println("WARNING: Unable to figure out what the fee is")
   260  	}
   261  	fmt.Println("Transaction Size:", tx.Size, "   NoWitSize:", tx.NoWitSize, "   Weight:", tx.Weight(), "   VSize:", tx.VSize())
   262  
   263  	if !tx.IsCoinBase() {
   264  		if unsigned > 0 {
   265  			fmt.Println("WARNING:", unsigned, "out of", len(tx.TxIn), "inputs are not signed or signed only patially")
   266  		} else {
   267  			fmt.Println("All", len(tx.TxIn), "transaction inputs seem to be signed")
   268  		}
   269  	}
   270  }