github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/wallet/stuff.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "strings" 10 11 "github.com/piotrnar/gocoin/lib/btc" 12 "github.com/piotrnar/gocoin/lib/others/ltc" 13 "github.com/piotrnar/gocoin/lib/others/sys" 14 ) 15 16 // loadedTxs is a cache for txs from already loaded from balance/ folder. 17 var loadedTxs map[[32]byte]*btc.Tx = make(map[[32]byte]*btc.Tx) 18 19 // getline reads a line from stdin. 20 func getline() string { 21 li, _, _ := bufio.NewReader(os.Stdin).ReadLine() 22 return string(li) 23 } 24 25 func ask_yes_no(msg string) bool { 26 for { 27 fmt.Print(msg, " (y/n) : ") 28 l := strings.ToLower(getline()) 29 if l == "y" { 30 return true 31 } else if l == "n" { 32 return false 33 } 34 } 35 } 36 37 func getpass() []byte { 38 var pass [1024]byte 39 var n int 40 var e error 41 var f *os.File 42 43 if stdin { 44 if *ask4pass { 45 fmt.Println("ERROR: Both -p and -stdin switches are not allowed at the same time") 46 return nil 47 } 48 d, er := ioutil.ReadAll(os.Stdin) 49 if er != nil { 50 fmt.Println("Reading from stdin:", e.Error()) 51 return nil 52 } 53 n = len(d) 54 if n <= 0 { 55 return nil 56 } 57 copy(pass[:n], d) 58 sys.ClearBuffer(d) 59 goto check_pass 60 } 61 62 if !*ask4pass { 63 f, e = os.Open(PassSeedFilename) 64 if e == nil { 65 n, _ = f.Read(pass[:]) 66 f.Close() 67 if n <= 0 { 68 return nil 69 } 70 goto check_pass 71 } 72 73 fmt.Println("Seed file", PassSeedFilename, "not found") 74 } 75 76 fmt.Print("Enter your wallet's seed password: ") 77 n = sys.ReadPassword(pass[:]) 78 if n <= 0 { 79 return nil 80 } 81 82 if *list { 83 if !*singleask { 84 fmt.Print("Re-enter the seed password (to be sure): ") 85 var pass2 [1024]byte 86 p2len := sys.ReadPassword(pass2[:]) 87 if p2len != n || !bytes.Equal(pass[:n], pass2[:p2len]) { 88 sys.ClearBuffer(pass[:n]) 89 sys.ClearBuffer(pass2[:p2len]) 90 println("The two passwords you entered do not match") 91 return nil 92 } 93 sys.ClearBuffer(pass2[:p2len]) 94 } 95 if *list { 96 // Maybe he wants to save the password? 97 if ask_yes_no("Save the password on disk, so you won't be asked for it later?") { 98 e = ioutil.WriteFile(PassSeedFilename, pass[:n], 0600) 99 if e != nil { 100 fmt.Println("WARNING: Could not save the password", e.Error()) 101 } else { 102 fmt.Println("The seed password has been stored in", PassSeedFilename) 103 } 104 } 105 } 106 } 107 check_pass: 108 for i := 0; i < n; i++ { 109 if pass[i] < ' ' || pass[i] > 126 { 110 fmt.Println("WARNING: Your secret contains non-printable characters") 111 break 112 } 113 } 114 outpass := make([]byte, n+len(secret_seed)) 115 if len(secret_seed) > 0 { 116 copy(outpass, secret_seed) 117 } 118 copy(outpass[len(secret_seed):], pass[:n]) 119 sys.ClearBuffer(pass[:n]) 120 return outpass 121 } 122 123 // get_change_addr returns the change addrress or nil if there will be no change. 124 func get_change_addr() (chng *btc.BtcAddr) { 125 if *change != "" { 126 var e error 127 chng, e = btc.NewAddrFromString(*change) 128 if e != nil { 129 println("Change address:", e.Error()) 130 cleanExit(1) 131 } 132 assert_address_version(chng) 133 return 134 } 135 136 // If change address not specified, send it back to the first input 137 for idx := range unspentOuts { 138 uo := getUO(&unspentOuts[idx].TxPrevOut) 139 if k := pkscr_to_key(uo.Pk_script); k != nil { 140 chng = btc.NewAddrFromPkScript(uo.Pk_script, testnet) 141 //chng = k.BtcAddr 142 return 143 } 144 } 145 146 fmt.Println("ERROR: Could not determine address where to send change. Add -change switch") 147 cleanExit(1) 148 return 149 } 150 151 func raw_tx_from_file(fn string) *btc.Tx { 152 dat := sys.GetRawData(fn) 153 if dat == nil { 154 fmt.Println("Cannot fetch raw transaction data") 155 return nil 156 } 157 tx, txle := btc.NewTx(dat) 158 if tx != nil { 159 tx.SetHash(dat) 160 if txle != len(dat) { 161 fmt.Println("WARNING: Raw transaction length mismatch", txle, len(dat)) 162 } 163 } 164 return tx 165 } 166 167 // tx_from_balance gets the tx for the given ID from the balance folder, or from cache. 168 func tx_from_balance(txid *btc.Uint256, error_is_fatal bool) (tx *btc.Tx) { 169 if tx = loadedTxs[txid.Hash]; tx != nil { 170 return // we have it in cache already 171 } 172 fn := "balance/" + txid.String() + ".tx" 173 buf, er := ioutil.ReadFile(fn) 174 if er == nil && buf != nil { 175 tx, _ = btc.NewTx(buf) 176 if error_is_fatal && tx == nil { 177 println("Transaction is corrupt:", txid.String()) 178 cleanExit(1) 179 } 180 tx.SetHash(buf) 181 if txid.Hash != tx.Hash.Hash { 182 println("Transaction ID mismatch:", txid.String(), tx.Hash.String()) 183 cleanExit(1) 184 } 185 } else if error_is_fatal { 186 println("Error reading transaction file:", fn) 187 if er != nil { 188 println(er.Error()) 189 } 190 cleanExit(1) 191 } 192 loadedTxs[txid.Hash] = tx // store it in the cache 193 return 194 } 195 196 // getUO looks for specific TxPrevOut in the balance folder. 197 func getUO(pto *btc.TxPrevOut) *btc.TxOut { 198 if _, ok := loadedTxs[pto.Hash]; !ok { 199 loadedTxs[pto.Hash] = tx_from_balance(btc.NewUint256(pto.Hash[:]), true) 200 } 201 return loadedTxs[pto.Hash].TxOut[pto.Vout] 202 } 203 204 // ver_pubkey returns the version byte for P2KH addresses. 205 func ver_pubkey() byte { 206 if litecoin { 207 return ltc.AddrVerPubkey(testnet) 208 } else { 209 return btc.AddrVerPubkey(testnet) 210 } 211 } 212 213 // ver_script returns the version byte for P2SH addresses. 214 func ver_script() byte { 215 if litecoin { 216 return ltc.AddrVerScript(testnet) 217 } else { 218 return btc.AddrVerScript(testnet) 219 } 220 } 221 222 // ver_secret returns the version byte for private key addresses. 223 func ver_secret() byte { 224 return ver_pubkey() + 0x80 225 } 226 227 // addr_from_pkscr gets the BtcAddr from pk_script. 228 func addr_from_pkscr(scr []byte) *btc.BtcAddr { 229 if litecoin { 230 return ltc.NewAddrFromPkScript(scr, testnet) 231 } else { 232 return btc.NewAddrFromPkScript(scr, testnet) 233 } 234 } 235 236 // assert_address_version makes sure the version byte in the given address is what we expect. 237 func assert_address_version(a *btc.BtcAddr) { 238 if a.SegwitProg != nil { 239 if a.SegwitProg.HRP != btc.GetSegwitHRP(testnet) { 240 println("Sending address", a.String(), "has an incorrect HRP string", a.SegwitProg.HRP) 241 cleanExit(1) 242 } 243 } else if a.Version != ver_pubkey() && a.Version != ver_script() { 244 println("Sending address", a.String(), "has an incorrect version", a.Version) 245 cleanExit(1) 246 } 247 }