func lookupInputAccount(dbtx walletdb.ReadTx, w *Wallet, details *wtxmgr.TxDetails, deb wtxmgr.DebitRecord) uint32 { addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) // TODO: Debits should record which account(s?) they // debit from so this doesn't need to be looked up. prevOP := &details.MsgTx.TxIn[deb.Index].PreviousOutPoint prev, err := w.TxStore.TxDetails(txmgrNs, &prevOP.Hash) if err != nil { log.Errorf("Cannot query previous transaction details for %v: %v", prevOP.Hash, err) return 0 } if prev == nil { log.Errorf("Missing previous transaction %v", prevOP.Hash) return 0 } prevOut := prev.MsgTx.TxOut[prevOP.Index] _, addrs, _, err := txscript.ExtractPkScriptAddrs(prevOut.Version, prevOut.PkScript, w.chainParams) var inputAcct uint32 if err == nil && len(addrs) > 0 { inputAcct, err = w.Manager.AddrAccount(addrmgrNs, addrs[0]) } if err != nil { log.Errorf("Cannot fetch account for previous output %v: %v", prevOP, err) inputAcct = 0 } return inputAcct }
// MakeSecp256k1MultiSigScript creates a multi-signature script that can be // redeemed with nRequired signatures of the passed keys and addresses. If the // address is a P2PKH address, the associated pubkey is looked up by the wallet // if possible, otherwise an error is returned for a missing pubkey. // // This function only works with secp256k1 pubkeys and P2PKH addresses derived // from them. func (w *Wallet) MakeSecp256k1MultiSigScript(secp256k1Addrs []dcrutil.Address, nRequired int) ([]byte, error) { secp256k1PubKeys := make([]*dcrutil.AddressSecpPubKey, len(secp256k1Addrs)) var dbtx walletdb.ReadTx var addrmgrNs walletdb.ReadBucket defer func() { if dbtx != nil { dbtx.Rollback() } }() // The address list will made up either of addreseses (pubkey hash), for // which we need to look up the keys in wallet, straight pubkeys, or a // mixture of the two. for i, addr := range secp256k1Addrs { switch addr := addr.(type) { default: return nil, errors.New("cannot make multisig script for " + "a non-secp256k1 public key or P2PKH address") case *dcrutil.AddressSecpPubKey: secp256k1PubKeys[i] = addr case *dcrutil.AddressPubKeyHash: if addr.DSA(w.chainParams) != chainec.ECTypeSecp256k1 { return nil, errors.New("cannot make multisig " + "script for a non-secp256k1 P2PKH address") } if dbtx == nil { var err error dbtx, err = w.db.BeginReadTx() if err != nil { return nil, err } addrmgrNs = dbtx.ReadBucket(waddrmgrNamespaceKey) } addrInfo, err := w.Manager.Address(addrmgrNs, addr) if err != nil { return nil, err } serializedPubKey := addrInfo.(waddrmgr.ManagedPubKeyAddress). PubKey().Serialize() pubKeyAddr, err := dcrutil.NewAddressSecpPubKey( serializedPubKey, w.chainParams) if err != nil { return nil, err } secp256k1PubKeys[i] = pubKeyAddr } } return txscript.MultiSigScript(secp256k1PubKeys, nRequired) }
func lookupOutputChain(dbtx walletdb.ReadTx, w *Wallet, details *wtxmgr.TxDetails, cred wtxmgr.CreditRecord) (account uint32, internal bool) { addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) output := details.MsgTx.TxOut[cred.Index] _, addrs, _, err := txscript.ExtractPkScriptAddrs(output.Version, output.PkScript, w.chainParams) var ma waddrmgr.ManagedAddress if err == nil && len(addrs) > 0 { ma, err = w.Manager.Address(addrmgrNs, addrs[0]) } if err != nil { log.Errorf("Cannot fetch account for wallet output: %v", err) } else { account = ma.Account() internal = ma.Internal() } return }
func totalBalances(dbtx walletdb.ReadTx, w *Wallet, m map[uint32]dcrutil.Amount) error { addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) unspent, err := w.TxStore.UnspentOutputs(dbtx.ReadBucket(wtxmgrNamespaceKey)) if err != nil { return err } for i := range unspent { output := unspent[i] var outputAcct uint32 _, addrs, _, err := txscript.ExtractPkScriptAddrs( txscript.DefaultScriptVersion, output.PkScript, w.chainParams) if err == nil && len(addrs) > 0 { outputAcct, err = w.Manager.AddrAccount(addrmgrNs, addrs[0]) } if err == nil { _, ok := m[outputAcct] if ok { m[outputAcct] += output.Amount } } } return nil }
func (s *NotificationServer) notifyUnminedTransaction(dbtx walletdb.ReadTx, details *wtxmgr.TxDetails) { // Sanity check: should not be currently coalescing a notification for // mined transactions at the same time that an unmined tx is notified. if s.currentTxNtfn != nil { log.Tracef("Notifying unmined tx notification while creating notification for blocks") } defer s.mu.Unlock() s.mu.Lock() clients := s.transactions if len(clients) == 0 { return } unminedTxs := []TransactionSummary{makeTxSummary(dbtx, s.wallet, details)} unminedHashes, err := s.wallet.TxStore.UnminedTxHashes(dbtx.ReadBucket(wtxmgrNamespaceKey)) if err != nil { log.Errorf("Cannot fetch unmined transaction hashes: %v", err) return } bals := make(map[uint32]dcrutil.Amount) relevantAccounts(s.wallet, bals, unminedTxs) err = totalBalances(dbtx, s.wallet, bals) if err != nil { log.Errorf("Cannot determine balances for relevant accounts: %v", err) return } n := &TransactionNotifications{ UnminedTransactions: unminedTxs, UnminedTransactionHashes: unminedHashes, NewBalances: flattenBalanceMap(bals), } for _, c := range clients { c <- n } }
func (s *NotificationServer) notifyAttachedBlock(dbtx walletdb.ReadTx, block *wtxmgr.BlockMeta) { if s.currentTxNtfn == nil { s.currentTxNtfn = &TransactionNotifications{} } // Add block details if it wasn't already included for previously // notified mined transactions. n := len(s.currentTxNtfn.AttachedBlocks) if n == 0 || *s.currentTxNtfn.AttachedBlocks[n-1].Hash != block.Hash { s.currentTxNtfn.AttachedBlocks = append(s.currentTxNtfn.AttachedBlocks, Block{ Hash: &block.Hash, Height: block.Height, Timestamp: block.Time.Unix(), }) } // For now (until notification coalescing isn't necessary) just use // chain length to determine if this is the new best block. if s.wallet.ChainSynced() { if len(s.currentTxNtfn.DetachedBlocks) >= len(s.currentTxNtfn.AttachedBlocks) { return } } defer s.mu.Unlock() s.mu.Lock() clients := s.transactions if len(clients) == 0 { s.currentTxNtfn = nil return } // The UnminedTransactions field is intentionally not set. Since the // hashes of all detached blocks are reported, and all transactions // moved from a mined block back to unconfirmed are either in the // UnminedTransactionHashes slice or don't exist due to conflicting with // a mined transaction in the new best chain, there is no possiblity of // a new, previously unseen transaction appearing in unconfirmed. txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) unminedHashes, err := s.wallet.TxStore.UnminedTxHashes(txmgrNs) if err != nil { log.Errorf("Cannot fetch unmined transaction hashes: %v", err) return } s.currentTxNtfn.UnminedTransactionHashes = unminedHashes bals := make(map[uint32]dcrutil.Amount) for _, b := range s.currentTxNtfn.AttachedBlocks { relevantAccounts(s.wallet, bals, b.Transactions) } err = totalBalances(dbtx, s.wallet, bals) if err != nil { log.Errorf("Cannot determine balances for relevant accounts: %v", err) return } s.currentTxNtfn.NewBalances = flattenBalanceMap(bals) for _, c := range clients { c <- s.currentTxNtfn } s.currentTxNtfn = nil }