// SearchRawTransactionsAsync returns an instance of a type that can be used to // get the result of the RPC at some future time by invoking the Receive // function on the returned instance. // // See SearchRawTransactions for the blocking version and more details. func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int, reverse bool, filterAddrs []string) FutureSearchRawTransactionsResult { addr := address.EncodeAddress() verbose := btcjson.Int(0) cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, nil, &reverse, &filterAddrs) return c.sendCmd(cmd) }
func (l *LibbitcoinClient) FetchHistory2(address btc.Address, fromHeight uint32, callback func(interface{}, error)) { hash160 := address.ScriptAddress() var netID byte height := make([]byte, 4) binary.LittleEndian.PutUint32(height, fromHeight) address.ScriptAddress() switch reflect.TypeOf(address).String() { case "*btcutil.AddressPubKeyHash": if l.Params.Name == chaincfg.MainNetParams.Name { netID = byte(0) } else { netID = byte(111) } case "*btcutil.AddressScriptHash": if l.Params.Name == chaincfg.MainNetParams.Name { netID = byte(5) } else { netID = byte(196) } } req := []byte{} req = append(req, netID) req = append(req, hash160...) req = append(req, height...) go l.SendCommand("address.fetch_history2", req, callback) }
func (l *LibbitcoinClient) UnsubscribeAddress(address btc.Address) { l.lock.Lock() _, ok := l.subscriptions[address.String()] if ok { delete(l.subscriptions, address.String()) } l.lock.Unlock() }
// SearchRawTransactionsVerboseAsync returns an instance of a type that can be // used to get the result of the RPC at some future time by invoking the Receive // function on the returned instance. // // See SearchRawTransactionsVerbose for the blocking version and more details. func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip, count int, includePrevOut, reverse bool, filterAddrs *[]string) FutureSearchRawTransactionsVerboseResult { addr := address.EncodeAddress() verbose := btcjson.Int(1) var prevOut *int if includePrevOut { prevOut = btcjson.Int(1) } cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, prevOut, &reverse, filterAddrs) return c.sendCmd(cmd) }
func (l *LibbitcoinClient) RenewSubscription(address btc.Address, callback func(interface{})) { req := []byte{} req = append(req, byte(0)) req = append(req, byte(160)) req = append(req, address.ScriptAddress()...) go l.SendCommand("address.renew", req, nil) l.lock.Lock() l.subscriptions[address.String()] = subscription{ expiration: time.Now().Add(24 * time.Hour), callback: callback, } l.lock.Unlock() }
// FilterTransactionsByAddress returns all transactions currently in the // mempool that either create an output to the passed address or spend a // previously created ouput to the address. func (mp *txMemPool) FilterTransactionsByAddress(addr btcutil.Address) ([]*btcutil.Tx, error) { // Protect concurrent access. mp.RLock() defer mp.RUnlock() if txs, exists := mp.addrindex[addr.EncodeAddress()]; exists { addressTxs := make([]*btcutil.Tx, 0, len(txs)) for txHash := range txs { if txD, exists := mp.pool[txHash]; exists { addressTxs = append(addressTxs, txD.Tx) } } return addressTxs, nil } return nil, fmt.Errorf("address does not have any transactions in the pool") }
func (s *walletServer) NextAddress(ctx context.Context, req *pb.NextAddressRequest) ( *pb.NextAddressResponse, error) { var ( addr btcutil.Address err error ) switch req.Kind { case pb.NextAddressRequest_BIP0044_EXTERNAL: addr, err = s.wallet.NewAddress(req.Account) case pb.NextAddressRequest_BIP0044_INTERNAL: addr, err = s.wallet.NewChangeAddress(req.Account) default: return nil, grpc.Errorf(codes.InvalidArgument, "kind=%v", req.Kind) } if err != nil { return nil, translateError(err) } return &pb.NextAddressResponse{Address: addr.EncodeAddress()}, nil }
// TotalReceivedForAddr iterates through a wallet's transaction history, // returning the total amount of bitcoins received for a single wallet // address. func (w *Wallet) TotalReceivedForAddr(addr btcutil.Address, minConf int32) (btcutil.Amount, error) { syncBlock := w.Manager.SyncedTo() var ( addrStr = addr.EncodeAddress() amount btcutil.Amount stopHeight int32 ) if minConf > 0 { stopHeight = syncBlock.Height - minConf + 1 } else { stopHeight = -1 } err := w.TxStore.RangeTransactions(0, stopHeight, func(details []wtxmgr.TxDetails) (bool, error) { for i := range details { detail := &details[i] for _, cred := range detail.Credits { pkScript := detail.MsgTx.TxOut[cred.Index].PkScript _, addrs, _, err := txscript.ExtractPkScriptAddrs( pkScript, w.chainParams) // An error creating addresses from the output script only // indicates a non-standard script, so ignore this credit. if err != nil { continue } for _, a := range addrs { if addrStr == a.EncodeAddress() { amount += cred.Amount break } } } } return false, nil }) return amount, err }
// PayToAddrScript creates a new script to pay a transaction output to a the // specified address. func PayToAddrScript(addr btcutil.Address) ([]byte, error) { switch addr := addr.(type) { case *btcutil.AddressPubKeyHash: if addr == nil { return nil, ErrUnsupportedAddress } return payToPubKeyHashScript(addr.ScriptAddress()) case *btcutil.AddressScriptHash: if addr == nil { return nil, ErrUnsupportedAddress } return payToScriptHashScript(addr.ScriptAddress()) case *btcutil.AddressPubKey: if addr == nil { return nil, ErrUnsupportedAddress } return payToPubKeyScript(addr.ScriptAddress()) } return nil, ErrUnsupportedAddress }
// addrToKey converts known address types to an addrindex key. An error is // returned for unsupported types. func addrToKey(addr btcutil.Address) ([addrKeySize]byte, error) { switch addr := addr.(type) { case *btcutil.AddressPubKeyHash: var result [addrKeySize]byte result[0] = addrKeyTypePubKeyHash copy(result[1:], addr.Hash160()[:]) return result, nil case *btcutil.AddressScriptHash: var result [addrKeySize]byte result[0] = addrKeyTypeScriptHash copy(result[1:], addr.Hash160()[:]) return result, nil case *btcutil.AddressPubKey: var result [addrKeySize]byte result[0] = addrKeyTypePubKeyHash copy(result[1:], addr.AddressPubKeyHash().Hash160()[:]) return result, nil } return [addrKeySize]byte{}, errUnsupportedAddressType }
// FetchTxsForAddr looks up and returns all transactions which either // spend from a previously created output of the passed address, or // create a new output locked to the passed address. The, `limit` parameter // should be the max number of transactions to be returned. Additionally, if the // caller wishes to seek forward in the results some amount, the 'seek' // represents how many results to skip. func (db *LevelDb) FetchTxsForAddr(addr btcutil.Address, skip int, limit int, reverse bool) ([]*database.TxListReply, int, error) { db.dbLock.Lock() defer db.dbLock.Unlock() // Enforce constraints for skip and limit. if skip < 0 { return nil, 0, errors.New("offset for skip must be positive") } if limit < 0 { return nil, 0, errors.New("value for limit must be positive") } // Parse address type, bailing on an unknown type. var addrKey []byte switch addr := addr.(type) { case *btcutil.AddressPubKeyHash: hash160 := addr.Hash160() addrKey = hash160[:] case *btcutil.AddressScriptHash: hash160 := addr.Hash160() addrKey = hash160[:] case *btcutil.AddressPubKey: hash160 := addr.AddressPubKeyHash().Hash160() addrKey = hash160[:] default: return nil, 0, database.ErrUnsupportedAddressType } // Create the prefix for our search. addrPrefix := make([]byte, 23, 23) copy(addrPrefix[0:3], addrIndexKeyPrefix) copy(addrPrefix[3:23], addrKey) iter := db.lDb.NewIterator(bytesPrefix(addrPrefix), nil) skipped := 0 if reverse { // Go to the last element if reverse iterating. iter.Last() // Skip "one past" the last element so the loops below don't // miss the last element due to Prev() being called first. // We can safely ignore iterator exhaustion since the loops // below will see there's no keys anyway. iter.Next() } for skip != 0 && advanceIterator(iter, reverse) { skip-- skipped++ } // Iterate through all address indexes that match the targeted prefix. var replies []*database.TxListReply var rawIndex [12]byte for advanceIterator(iter, reverse) && limit != 0 { copy(rawIndex[:], iter.Key()[23:35]) addrIndex := unpackTxIndex(rawIndex) tx, blkSha, blkHeight, _, err := db.fetchTxDataByLoc(addrIndex.blkHeight, addrIndex.txoffset, addrIndex.txlen, []byte{}) if err != nil { // Eat a possible error due to a potential re-org. continue } txSha := tx.TxSha() txReply := &database.TxListReply{Sha: &txSha, Tx: tx, BlkSha: blkSha, Height: blkHeight, TxSpent: []bool{}, Err: err} replies = append(replies, txReply) limit-- } iter.Release() if err := iter.Error(); err != nil { return nil, 0, err } return replies, skipped, nil }
// SearchRawTransactionsVerboseAsync returns an instance of a type that can be // used to get the result of the RPC at some future time by invoking the Receive // function on the returned instance. // // See SearchRawTransactionsVerbose for the blocking version and more details. func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip, count int) FutureSearchRawTransactionsVerboseResult { addr := address.EncodeAddress() verbose := btcjson.Int(1) cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count) return c.sendCmd(cmd) }
func (l *LibbitcoinClient) Parse(command string, data []byte, callback func(interface{}, error)) { switch command { case "address.fetch_history2": numRows := (len(data) - 4) / 49 buff := bytes.NewBuffer(data) err := ParseError(buff.Next(4)) rows := []FetchHistory2Resp{} for i := 0; i < numRows; i++ { r := FetchHistory2Resp{} spendByte := int(buff.Next(1)[0]) var spendBool bool if spendByte == 0 { spendBool = false } else { spendBool = true } r.IsSpend = spendBool lehash := buff.Next(32) sh, _ := wire.NewShaHash(lehash) r.TxHash = sh.String() indexBytes := buff.Next(4) r.Index = binary.LittleEndian.Uint32(indexBytes) heightBytes := buff.Next(4) r.Height = binary.LittleEndian.Uint32(heightBytes) valueBytes := buff.Next(8) r.Value = binary.LittleEndian.Uint64(valueBytes) rows = append(rows, r) } callback(rows, err) case "blockchain.fetch_last_height": height := binary.LittleEndian.Uint32(data[4:]) callback(height, ParseError(data[:4])) case "blockchain.fetch_transaction": txn, _ := btc.NewTxFromBytes(data[4:]) callback(txn, ParseError(data[:4])) case "transaction_pool.fetch_transaction": txn, _ := btc.NewTxFromBytes(data[4:]) callback(txn, ParseError(data[:4])) case "address.update": buff := bytes.NewBuffer(data) addressVersion := buff.Next(1)[0] addressHash160 := buff.Next(20) height := buff.Next(4) block := buff.Next(32) tx := buff.Bytes() var addr btc.Address if addressVersion == byte(111) || addressVersion == byte(0) { a, _ := btc.NewAddressPubKeyHash(addressHash160, l.Params) addr = a } else if addressVersion == byte(5) || addressVersion == byte(196) { a, _ := btc.NewAddressScriptHashFromHash(addressHash160, l.Params) addr = a } bl, _ := wire.NewShaHash(block) txn, _ := btc.NewTxFromBytes(tx) resp := SubscribeResp{ Address: addr.String(), Height: binary.LittleEndian.Uint32(height), Block: bl.String(), Tx: *txn, } l.lock.Lock() l.subscriptions[addr.String()].callback(resp) l.lock.Unlock() case "protocol.broadcast_transaction": callback(nil, ParseError(data[:4])) case "transaction_pool.validate": b := data[4:5] success, _ := strconv.ParseBool(string(b)) callback(success, ParseError(data[:4])) } }
// FetchTxsForAddr looks up and returns all transactions which either // spend from a previously created output of the passed address, or // create a new output locked to the passed address. The, `limit` parameter // should be the max number of transactions to be returned. Additionally, if the // caller wishes to seek forward in the results some amount, the 'seek' // represents how many results to skip. func (db *LevelDb) FetchTxsForAddr(addr btcutil.Address, skip int, limit int) ([]*database.TxListReply, error) { db.dbLock.Lock() defer db.dbLock.Unlock() // Enforce constraints for skip and limit. if skip < 0 { return nil, errors.New("offset for skip must be positive") } if limit < 0 { return nil, errors.New("value for limit must be positive") } // Parse address type, bailing on an unknown type. var addrKey []byte switch addr := addr.(type) { case *btcutil.AddressPubKeyHash: hash160 := addr.Hash160() addrKey = hash160[:] case *btcutil.AddressScriptHash: hash160 := addr.Hash160() addrKey = hash160[:] case *btcutil.AddressPubKey: hash160 := addr.AddressPubKeyHash().Hash160() addrKey = hash160[:] default: return nil, database.ErrUnsupportedAddressType } // Create the prefix for our search. addrPrefix := make([]byte, 23, 23) copy(addrPrefix[0:3], addrIndexKeyPrefix) copy(addrPrefix[3:23], addrKey) iter := db.lDb.NewIterator(bytesPrefix(addrPrefix), nil) for skip != 0 && iter.Next() { skip-- } // Iterate through all address indexes that match the targeted prefix. var replies []*database.TxListReply var rawIndex [12]byte for iter.Next() && limit != 0 { copy(rawIndex[:], iter.Key()[23:35]) addrIndex := unpackTxIndex(rawIndex) tx, blkSha, blkHeight, _, err := db.fetchTxDataByLoc(addrIndex.blkHeight, addrIndex.txoffset, addrIndex.txlen, []byte{}) if err != nil { // Eat a possible error due to a potential re-org. continue } txSha := tx.TxSha() txReply := &database.TxListReply{Sha: &txSha, Tx: tx, BlkSha: blkSha, Height: blkHeight, TxSpent: []bool{}, Err: err} replies = append(replies, txReply) limit-- } iter.Release() if err := iter.Error(); err != nil { return nil, err } return replies, nil }
func (w *LibbitcoinWallet) Spend(amount int64, addr btc.Address, feeLevel bitcoin.FeeLevel) error { // Check for dust script, _ := txscript.PayToAddrScript(addr) if txrules.IsDustAmount(btc.Amount(amount), len(script), txrules.DefaultRelayFeePerKb) { return errors.New("Amount is below dust threshold") } var additionalPrevScripts map[wire.OutPoint][]byte var additionalKeysByAddress map[string]*btc.WIF // Create input source coinMap := w.gatherCoins() coins := make([]coinset.Coin, 0, len(coinMap)) for k := range coinMap { coins = append(coins, k) } inputSource := func(target btc.Amount) (total btc.Amount, inputs []*wire.TxIn, scripts [][]byte, err error) { // TODO: maybe change the coin selection algorithm? We're using min coins right now because // TODO: we don't know the number of confirmations on each coin without querying the libbitcoin server. coinSelector := coinset.MinNumberCoinSelector{MaxInputs: 10000, MinChangeAmount: btc.Amount(10000)} coins, err := coinSelector.CoinSelect(target, coins) if err != nil { return total, inputs, scripts, errors.New("insuffient funds") } additionalPrevScripts = make(map[wire.OutPoint][]byte) additionalKeysByAddress = make(map[string]*btc.WIF) for _, c := range coins.Coins() { total += c.Value() outpoint := wire.NewOutPoint(c.Hash(), c.Index()) in := wire.NewTxIn(outpoint, []byte{}) in.Sequence = 0 // Opt-in RBF so we can bump fees inputs = append(inputs, in) additionalPrevScripts[*outpoint] = c.PkScript() key := coinMap[c] addr, _ := btc.NewAddressPubKey(key.PublicKey().Key, w.params) pk, _ := btcec.PrivKeyFromBytes(btcec.S256(), key.Key) wif, _ := btc.NewWIF(pk, w.params, true) additionalKeysByAddress[addr.AddressPubKeyHash().EncodeAddress()] = wif } return total, inputs, scripts, nil } // Get the fee per kilobyte feePerKB := int64(w.getFeePerByte(feeLevel)) * 1000 // outputs out := wire.NewTxOut(amount, script) // Create change source changeSource := func() ([]byte, error) { addr := w.GetCurrentAddress(bitcoin.CHANGE) script, err := txscript.PayToAddrScript(addr) if err != nil { return []byte{}, err } return script, nil } authoredTx, err := txauthor.NewUnsignedTransaction([]*wire.TxOut{out}, btc.Amount(feePerKB), inputSource, changeSource) if err != nil { return err } // BIP 69 sorting txsort.InPlaceSort(authoredTx.Tx) // Sign tx getKey := txscript.KeyClosure(func(addr btc.Address) ( *btcec.PrivateKey, bool, error) { addrStr := addr.EncodeAddress() wif := additionalKeysByAddress[addrStr] return wif.PrivKey, wif.CompressPubKey, nil }) getScript := txscript.ScriptClosure(func( addr btc.Address) ([]byte, error) { return []byte{}, nil }) for i, txIn := range authoredTx.Tx.TxIn { prevOutScript := additionalPrevScripts[txIn.PreviousOutPoint] script, err := txscript.SignTxOutput(w.params, authoredTx.Tx, i, prevOutScript, txscript.SigHashAll, getKey, getScript, txIn.SignatureScript) if err != nil { return errors.New("Failed to sign transaction") } txIn.SignatureScript = script } // Broadcast tx to bitcoin network serializedTx := new(bytes.Buffer) authoredTx.Tx.Serialize(serializedTx) w.Client.Broadcast(serializedTx.Bytes(), func(i interface{}, err error) { if err == nil { log.Infof("Broadcast tx %s to bitcoin network\n", authoredTx.Tx.TxSha().String()) } else { log.Errorf("Failed to broadcast tx, reason: %s\n", err) } }) // Update the db w.ProcessTransaction(btc.NewTx(authoredTx.Tx), 0) return nil }