func (db *unspentDb) del(idx *btc.TxPrevOut) { if db.ch.CB.NotifyTx != nil { db.ch.CB.NotifyTx(idx, nil) } key := qdb.KeyType(idx.UIdx()) db.dbN(int(idx.Hash[31]) % NumberOfUnspentSubDBs).Del(key) }
func (db *unspentDb) add(idx *btc.TxPrevOut, Val_Pk *btc.TxOut) { v := make([]byte, SCR_OFFS+len(Val_Pk.Pk_script)) copy(v[0:32], idx.Hash[:]) binary.LittleEndian.PutUint32(v[32:36], idx.Vout) binary.LittleEndian.PutUint64(v[36:44], Val_Pk.Value) binary.LittleEndian.PutUint32(v[44:48], Val_Pk.BlockHeight) copy(v[SCR_OFFS:], Val_Pk.Pk_script) k := qdb.KeyType(idx.UIdx()) var flgz uint32 dbN := db.dbN(int(idx.Hash[31]) % NumberOfUnspentSubDBs) if stealthIndex(v) { if db.ch.CB.NotifyStealthTx != nil { db.ch.CB.NotifyStealthTx(dbN, k, NewWalkRecord(v)) } flgz = qdb.YES_CACHE | qdb.YES_BROWSE } else { if db.ch.CB.NotifyTx != nil { db.ch.CB.NotifyTx(idx, Val_Pk) } if Val_Pk.Value < MinBrowsableOutValue { flgz = qdb.NO_CACHE | qdb.NO_BROWSE } else if NocacheBlocksBelow == -1 { flgz = qdb.NO_CACHE | qdb.NO_BROWSE } } dbN.PutExt(k, v, flgz) }
// Return txs in mempool that are spending any outputs form the given tx func findPendingTxs(tx *btc.Tx) (res []BIDX) { var in btc.TxPrevOut copy(in.Hash[:], tx.Hash.Hash[:]) for in.Vout = 0; in.Vout < uint32(len(tx.TxOut)); in.Vout++ { if r, ok := SpentOutputs[in.UIdx()]; ok { res = append(res, r) } } return res }
// This is called while accepting the block (from the chain's thread) func TxNotify(idx *btc.TxPrevOut, valpk *btc.TxOut) { var update_wallet bool BalanceMutex.Lock() if valpk != nil { // Extract hash160 from pkscript adr := btc.NewAddrFromPkScript(valpk.Pk_script, common.Testnet) if adr != nil { if rec, ok := CachedAddrs[adr.Hash160]; ok { rec.Value += valpk.Value utxo := new(chain.OneUnspentTx) utxo.TxPrevOut = *idx utxo.Value = valpk.Value utxo.MinedAt = valpk.BlockHeight utxo.BtcAddr = CacheUnspent[rec.CacheIndex].BtcAddr CacheUnspent[rec.CacheIndex].AllUnspentTx = append(CacheUnspent[rec.CacheIndex].AllUnspentTx, utxo) CacheUnspentIdx[idx.UIdx()] = &OneCachedUnspentIdx{Index: rec.CacheIndex, Record: utxo} if rec.InWallet { update_wallet = true } } } } else { ii := idx.UIdx() if ab, present := CacheUnspentIdx[ii]; present { adrec := CacheUnspent[ab.Index] //println("removing", idx.String()) rec := CachedAddrs[adrec.BtcAddr.Hash160] if rec == nil { panic("rec not found for " + adrec.BtcAddr.String()) } rec.Value -= ab.Record.Value if rec.InWallet { update_wallet = true } for j := range adrec.AllUnspentTx { if adrec.AllUnspentTx[j] == ab.Record { //println("found it at index", j) adrec.AllUnspentTx = append(adrec.AllUnspentTx[:j], adrec.AllUnspentTx[j+1:]...) break } } delete(CacheUnspentIdx, ii) } } if update_wallet { sync_wallet() } BalanceMutex.Unlock() }
func (db *unspentDb) get(po *btc.TxPrevOut) (res *btc.TxOut, e error) { ind := qdb.KeyType(po.UIdx()) val := db.dbN(int(po.Hash[31]) % NumberOfUnspentSubDBs).Get(ind) if val == nil { e = errors.New("Unspent not found") return } if len(val) < SCR_OFFS { panic(fmt.Sprint("unspent record too short:", len(val))) } res = new(btc.TxOut) res.Value = binary.LittleEndian.Uint64(val[36:44]) res.BlockHeight = binary.LittleEndian.Uint32(val[44:48]) res.Pk_script = make([]byte, len(val)-SCR_OFFS) copy(res.Pk_script, val[SCR_OFFS:]) return }
// This is called while accepting the block (from the chain's thread) func TxNotifyDel(txid []byte, outs []bool) { var update_wallet bool BalanceMutex.Lock() var uidx btc.TxPrevOut copy(uidx.Hash[:], txid) for uidx.Vout = 0; uidx.Vout < uint32(len(outs)); uidx.Vout++ { if outs[uidx.Vout] { ii := uidx.UIdx() if ab, present := CacheUnspentIdx[ii]; present { adrec := CacheUnspent[ab.Index] rec := CachedAddrs[adrec.BtcAddr.Hash160] if rec == nil { panic("rec not found for " + adrec.BtcAddr.String()) } rec.Value -= ab.Record.Value if rec.InWallet { update_wallet = true } for j := range adrec.AllUnspentTx { if adrec.AllUnspentTx[j] == ab.Record { //println("found it at index", j) adrec.AllUnspentTx = append(adrec.AllUnspentTx[:j], adrec.AllUnspentTx[j+1:]...) break } } delete(CacheUnspentIdx, ii) } } } if update_wallet { sync_wallet() } BalanceMutex.Unlock() }
// This isusually the most time consuming process when applying a new block func (ch *Chain) commitTxs(bl *btc.Block, changes *BlockChanges) (e error) { sumblockin := btc.GetBlockReward(changes.Height) var txoutsum, txinsum, sumblockout uint64 // Add each tx outs from the current block to the temporary pool blUnsp := make(map[[32]byte][]*btc.TxOut, len(bl.Txs)) for i := range bl.Txs { outs := make([]*btc.TxOut, len(bl.Txs[i].TxOut)) for j := range bl.Txs[i].TxOut { bl.Txs[i].TxOut[j].BlockHeight = changes.Height outs[j] = bl.Txs[i].TxOut[j] } blUnsp[bl.Txs[i].Hash.Hash] = outs } // create a channnel to receive results from VerifyScript threads: done := make(chan bool, sys.UseThreads) for i := range bl.Txs { txoutsum, txinsum = 0, 0 // Check each tx for a valid input, except from the first one if i > 0 { tx_trusted := bl.Trusted if !tx_trusted && TrustedTxChecker != nil && TrustedTxChecker(bl.Txs[i].Hash) { tx_trusted = true } scripts_ok := true for j := 0; j < sys.UseThreads; j++ { done <- true } for j := 0; j < len(bl.Txs[i].TxIn); /*&& e==nil*/ j++ { inp := &bl.Txs[i].TxIn[j].Input if _, ok := changes.DeledTxs[*inp]; ok { println("txin", inp.String(), "already spent in this block") e = errors.New("Input spent more then once in same block") break } tout := ch.PickUnspent(inp) if tout == nil { t, ok := blUnsp[inp.Hash] if !ok { e = errors.New("Unknown input TxID: " + btc.NewUint256(inp.Hash[:]).String()) break } if inp.Vout >= uint32(len(t)) { println("Vout too big", len(t), inp.String()) e = errors.New("Vout too big") break } if t[inp.Vout] == nil { println("Vout already spent", inp.String()) e = errors.New("Vout already spent") break } tout = t[inp.Vout] t[inp.Vout] = nil // and now mark it as spent: } if !(<-done) { println("VerifyScript error 1") scripts_ok = false break } if tx_trusted { done <- true } else { go func(sig []byte, prv []byte, i int, tx *btc.Tx) { done <- script.VerifyTxScript(sig, prv, i, tx, bl.BlockTime() >= BIP16SwitchTime) }(bl.Txs[i].TxIn[j].ScriptSig, tout.Pk_script, j, bl.Txs[i]) } // Verify Transaction script: txinsum += tout.Value changes.DeledTxs[*inp] = tout } if scripts_ok { scripts_ok = <-done } for j := 1; j < sys.UseThreads; j++ { if !(<-done) { println("VerifyScript error 2") scripts_ok = false } } if len(done) != 0 { panic("ASSERT: The channel should be empty gere") } if !scripts_ok { return errors.New("VerifyScripts failed") } } else { // For coinbase tx we need to check (like satoshi) whether the script size is between 2 and 100 bytes // (Previously we made sure in CheckBlock() that this was a coinbase type tx) if len(bl.Txs[0].TxIn[0].ScriptSig) < 2 || len(bl.Txs[0].TxIn[0].ScriptSig) > 100 { return errors.New(fmt.Sprint("Coinbase script has a wrong length", len(bl.Txs[0].TxIn[0].ScriptSig))) } } sumblockin += txinsum for j := range bl.Txs[i].TxOut { txoutsum += bl.Txs[i].TxOut[j].Value txa := new(btc.TxPrevOut) copy(txa.Hash[:], bl.Txs[i].Hash.Hash[:]) txa.Vout = uint32(j) _, spent := changes.DeledTxs[*txa] if spent { delete(changes.DeledTxs, *txa) } else { changes.AddedTxs[*txa] = bl.Txs[i].TxOut[j] } } sumblockout += txoutsum if e != nil { return // If any input fails, do not continue } if i > 0 && txoutsum > txinsum { return errors.New(fmt.Sprintf("More spent (%.8f) than at the input (%.8f) in TX %s", float64(txoutsum)/1e8, float64(txinsum)/1e8, bl.Txs[i].Hash.String())) } } if sumblockin < sumblockout { return errors.New(fmt.Sprintf("Out:%d > In:%d", sumblockout, sumblockin)) } return nil }
// load the content of the "balance/" folder func load_balance(showbalance bool) { var unknownInputs, multisigInputs int f, e := os.Open("balance/unspent.txt") if e != nil { println(e.Error()) return } rd := bufio.NewReader(f) for { l, _, e := rd.ReadLine() if len(l) == 0 && e != nil { break } if l[64] == '-' { txid := btc.NewUint256FromString(string(l[:64])) rst := strings.SplitN(string(l[65:]), " ", 2) vout, _ := strconv.ParseUint(rst[0], 10, 32) uns := new(btc.TxPrevOut) copy(uns.Hash[:], txid.Hash[:]) uns.Vout = uint32(vout) lab := "" if len(rst) > 1 { lab = rst[1] } str := string(l) if sti := strings.Index(str, "_StealthC:"); sti != -1 { c, e := hex.DecodeString(str[sti+10 : sti+10+64]) if e != nil { fmt.Println("ERROR at stealth", txid.String(), vout, e.Error()) } else { // add a new key to the wallet sec := btc.DeriveNextPrivate(first_seed[:], c) is_stealth[len(priv_keys)] = true priv_keys = append(priv_keys, sec) labels = append(labels, lab) pub_key := btc.PublicFromPrivate(sec, true) publ_addrs = append(publ_addrs, btc.NewAddrFromPubkey(pub_key, AddrVerPubkey())) compressed_key = append(compressed_key, true) // stealth keys are always compressed } } if _, ok := loadedTxs[txid.Hash]; !ok { tf, _ := os.Open("balance/" + txid.String() + ".tx") if tf != nil { siz, _ := tf.Seek(0, os.SEEK_END) tf.Seek(0, os.SEEK_SET) buf := make([]byte, siz) tf.Read(buf) tf.Close() th := btc.Sha2Sum(buf) if bytes.Equal(th[:], txid.Hash[:]) { tx, _ := btc.NewTx(buf) if tx != nil { loadedTxs[txid.Hash] = tx } else { println("transaction is corrupt:", txid.String()) } } else { println("transaction file is corrupt:", txid.String()) os.Exit(1) } } else { println("transaction file not found:", txid.String()) os.Exit(1) } } // Sum up all the balance and check if we have private key for this input uo := UO(uns) add_it := true if !btc.IsP2SH(uo.Pk_script) { fnd := false for j := range publ_addrs { if publ_addrs[j].Owns(uo.Pk_script) { fnd = true break } } if !fnd { if *onlvalid { add_it = false } if showbalance { unknownInputs++ if *verbose { ss := uns.String() ss = ss[:8] + "..." + ss[len(ss)-12:] fmt.Println(ss, "does not belong to your wallet (cannot sign it)") } } } } else { if *onlvalid { add_it = false } if *verbose { ss := uns.String() ss = ss[:8] + "..." + ss[len(ss)-12:] fmt.Println(ss, "belongs to a multisig address") } multisigInputs++ } if add_it { unspentOuts = append(unspentOuts, uns) unspentOutsLabel = append(unspentOutsLabel, lab) totBtc += UO(uns).Value } } } f.Close() fmt.Printf("You have %.8f BTC in %d unspent outputs. %d inputs are multisig type\n", float64(totBtc)/1e8, len(unspentOuts), multisigInputs) if showbalance { if unknownInputs > 0 { fmt.Printf("WARNING: Some inputs (%d) cannot be spent with this password (-v to print them)\n", unknownInputs) } } }