func do_scan_stealth(p string, ignore_prefix bool) { sa, _ := btc.NewStealthAddrFromString(p) if sa == nil { fmt.Println("Specify base58 encoded stealth address") return } if sa.Version != btc.StealthAddressVersion(common.CFG.Testnet) { fmt.Println("Incorrect version of the stealth address") return } if len(sa.SpendKeys) != 1 { fmt.Println("Currently only single spend keys are supported. This address has", len(sa.SpendKeys)) return } //fmt.Println("scankey", hex.EncodeToString(sa.ScanKey[:])) if ignore_prefix { sa.Prefix = []byte{0} fmt.Println("Ignoring Prefix inside the address") } else if len(sa.Prefix) == 0 { fmt.Println("Prefix not present in the address") } else { fmt.Println("Prefix", sa.Prefix[0], hex.EncodeToString(sa.Prefix[1:])) } ds := wallet.FetchStealthKeys() if len(ds) == 0 { return } defer func() { for i := range ds { utils.ClearBuffer(ds[i]) } }() // clear the keys in mem after all var d []byte for i := range ds { if bytes.Equal(btc.PublicFromPrivate(ds[i], true), sa.ScanKey[:]) { d = ds[i] } } if d == nil { fmt.Println("No matching secret found your wallet/stealth folder") return } var pos []*btc.TxPrevOut cs := make(map[uint64][]byte) as := make(map[uint64]*btc.BtcAddr) var ncnt uint common.BlockChain.Unspent.ScanStealth(sa, func(eth, txid []byte, vout uint32, scr []byte) bool { if len(scr) == 25 && scr[0] == 0x76 && scr[1] == 0xa9 && scr[2] == 0x14 && scr[23] == 0x88 && scr[24] == 0xac { var h160 [20]byte //yes := btc.NewUint256(txid).String()=="9cc90ff2528b49dfd9c53e5e90c98a1fd45d577af7f3a9e7a9f8a86b52fb0280" c := btc.StealthDH(eth, d) spen_exp := btc.DeriveNextPublic(sa.SpendKeys[0][:], c) btc.RimpHash(spen_exp, h160[:]) if bytes.Equal(scr[3:23], h160[:]) { po := new(btc.TxPrevOut) copy(po.Hash[:], txid) po.Vout = vout pos = append(pos, po) cs[po.UIdx()] = c as[po.UIdx()] = btc.NewAddrFromHash160(h160[:], btc.AddrVerPubkey(common.CFG.Testnet)) } ncnt++ /*fmt.Printf("%s with c=%s", btc.NewAddrFromHash160(h160[:], btc.AddrVerPubkey(common.CFG.Testnet)).String(), hex.EncodeToString(c)) fmt.Println()*/ return true } else { return false } }) fmt.Println(len(pos), "outputs, out of", ncnt, "notifications belonged to our wallet") var unsp btc.AllUnspentTx for i := range pos { po, e := common.BlockChain.Unspent.UnspentGet(pos[i]) if e != nil { println("UnspentGet:", e.Error()) println("This should not happen - please, report a bug.") println("You can probably fix it by launching the client with -rescan") os.Exit(1) } //fmt.Println(btc.NewUint256(pos[i].Hash[:]), pos[i].Vout+1, hex.EncodeToString(cs[pos[i].UIdx()])) one := &btc.OneUnspentTx{ TxPrevOut: *pos[i], Value: po.Value, MinedAt: po.BlockHeight, BtcAddr: as[pos[i].UIdx()], StealthC: cs[pos[i].UIdx()]} unsp = append(unsp, one) } sort.Sort(unsp) os.RemoveAll("balance") os.MkdirAll("balance/", 0770) utxt, _ := os.Create("balance/unspent.txt") fmt.Print(wallet.DumpBalance(unsp, utxt, true, false)) }
// Thanks @dabura667 - https://bitcointalk.org/index.php?topic=590349.msg6560332#msg6560332 func stealth_txout(sa *btc.StealthAddr, value uint64) (res []*btc.TxOut) { if sa.Version != btc.StealthAddressVersion(*testnet) { fmt.Println("ERROR: Unsupported version of a stealth address", sa.Version) os.Exit(1) } if len(sa.SpendKeys) != 1 { fmt.Println("ERROR: Currently only non-multisig stealth addresses are supported", len(sa.SpendKeys)) os.Exit(1) } // Make two outpus res = make([]*btc.TxOut, 2) var e, ephemkey, pkscr []byte var nonce, nonce_from uint32 var look4pref bool sha := sha256.New() // 6. create a new pub/priv keypair (lets call its pubkey "ephemkey" and privkey "e") pick_different_e: e = make([]byte, 32) rand.Read(e) defer utils.ClearBuffer(e) ephemkey = btc.PublicFromPrivate(e, true) if *verbose { fmt.Println("e", hex.EncodeToString(e)) fmt.Println("ephemkey", hex.EncodeToString(ephemkey)) } // 7. IF there is a prefix in the stealth address, brute force a nonce such // that SHA256(nonce.concate(ephemkey)) first 4 bytes are equal to the prefix. // IF NOT, then just run through the loop once and pickup a random nonce. // (probably make the while condition include "or prefix = null" or something to that nature. look4pref = len(sa.Prefix) > 0 && sa.Prefix[0] > 0 if look4pref { fmt.Print("Prefix is ", sa.Prefix[0], ":", hex.EncodeToString(sa.Prefix[1:]), " - looking for nonce...") } binary.Read(rand.Reader, binary.LittleEndian, &nonce_from) nonce = nonce_from for { binary.Write(sha, binary.LittleEndian, nonce) sha.Write(ephemkey) if sa.CheckPrefix(sha.Sum(nil)[:4]) { break } sha.Reset() nonce++ if nonce == nonce_from { fmt.Println("EOF") goto pick_different_e } if (nonce & 0xfffff) == 0 { fmt.Print(".") } } if look4pref { fmt.Println(uint32(nonce - nonce_from)) } // 8. Once you have the nonce and the ephemkey, you can create the first output, which is pkscr = make([]byte, 40) pkscr[0] = 0x6a // OP_RETURN pkscr[1] = 38 // length pkscr[2] = 0x06 // always 6 binary.LittleEndian.PutUint32(pkscr[3:7], nonce) copy(pkscr[7:40], ephemkey) res[0] = &btc.TxOut{Pk_script: pkscr} // 9. Now use ECC multiplication to calculate e*Q where Q = scan_pubkey // an e = privkey to ephemkey and then hash it. c := btc.StealthDH(sa.ScanKey[:], e) if *verbose { fmt.Println("c", hex.EncodeToString(c)) } // 10. That hash is now "c". use ECC multiplication and addition to // calculate D + (c*G) where D = spend_pubkey, and G is the reference // point for secp256k1. This will give you a new pubkey. (we'll call it D') Dpr := btc.DeriveNextPublic(sa.SpendKeys[0][:], c) if *verbose { fmt.Println("Dpr", hex.EncodeToString(Dpr)) } // 11. Create a normal P2KH output spending to D' as public key. adr := btc.NewAddrFromPubkey(Dpr, btc.AddrVerPubkey(*testnet)) res[1] = &btc.TxOut{Value: value, Pk_script: adr.OutScript()} fmt.Println("Sending to stealth", adr.String()) return }