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 }