Documentation ¶
Overview ¶
Package wtxmgr provides an implementation of a transaction database handling spend tracking for a bitcoin wallet. Its primary purpose is to save transactions with outputs spendable with wallet keys and transactions that are signed by wallet keys in memory, handle spend tracking for unspent outputs and newly-inserted transactions, and report the spendable balance from each unspent transaction output. It uses walletdb as the backend for storing the serialized transaction objects in buckets.
Transaction outputs which are spendable by wallet keys are called credits (because they credit to a wallet's total spendable balance). Transaction inputs which spend previously-inserted credits are called debits (because they debit from the wallet's spendable balance).
Spend tracking is mostly automatic. When a new transaction is inserted, if it spends from any unspent credits, they are automatically marked spent by the new transaction, and each input which spent a credit is marked as a debit. However, transaction outputs of inserted transactions must manually marked as credits, as this package has no knowledge of wallet keys or addresses, and therefore cannot determine which outputs may be spent.
Details regarding individual transactions and their credits and debits may be queried either by just a transaction hash, or by hash and block. When querying for just a transaction hash, the most recent transaction with a matching hash will be queried. However, because transaction hashes may collide with other transaction hashes, methods to query for specific transactions in the chain (or unmined) are provided as well.
Example (BasicUsage) ¶
// Open the database. db, dbTeardown, err := testDB() defer dbTeardown() if err != nil { fmt.Println(err) return } // Open a read-write transaction to operate on the database. dbtx, err := db.BeginReadWriteTx() if err != nil { fmt.Println(err) return } defer dbtx.Commit() // Create a bucket for the transaction store. b, err := dbtx.CreateTopLevelBucket([]byte("txstore")) if err != nil { fmt.Println(err) return } // Create and open the transaction store in the provided namespace. err = wtxmgr.Create(b) if err != nil { fmt.Println(err) return } s, err := wtxmgr.Open(b, &chaincfg.TestNet3Params) if err != nil { fmt.Println(err) return } // Insert an unmined transaction that outputs 10 DUO to a wallet address // at output 0. err = s.InsertTx(b, exampleTxRecordA, nil) if err != nil { fmt.Println(err) return } err = s.AddCredit(b, exampleTxRecordA, nil, 0, false) if err != nil { fmt.Println(err) return } // Insert a second transaction which spends the output, and creates two // outputs. Mark the second one (5 DUO) as wallet change. err = s.InsertTx(b, exampleTxRecordB, nil) if err != nil { fmt.Println(err) return } err = s.AddCredit(b, exampleTxRecordB, nil, 1, true) if err != nil { fmt.Println(err) return } // Mine each transaction in a block at height 100. err = s.InsertTx(b, exampleTxRecordA, &exampleBlock100) if err != nil { fmt.Println(err) return } err = s.InsertTx(b, exampleTxRecordB, &exampleBlock100) if err != nil { fmt.Println(err) return } // Print the one confirmation balance. bal, err := s.Balance(b, 1, 100) if err != nil { fmt.Println(err) return } fmt.Println(bal) // Fetch unspent outputs. utxos, err := s.UnspentOutputs(b) if err != nil { fmt.Println(err) } expectedOutPoint := wire.OutPoint{Hash: exampleTxRecordB.Hash, Index: 1} for _, utxo := range utxos { fmt.Println(utxo.OutPoint == expectedOutPoint) }
Output:
Index ¶
- Constants
- Variables
- func Create(ns walletdb.ReadWriteBucket) error
- func DoUpgrades(db walletdb.DB, namespaceKey []byte) error
- func IsNoExists(err error) bool
- func UseLogger(logger *cl.SubSystem)
- type Block
- type BlockMeta
- type Credit
- type CreditRecord
- type DebitRecord
- type Error
- type ErrorCode
- type Store
- func (s *Store) AddCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, ...) error
- func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32) (util.Amount, error)
- func (s *Store) InsertTx(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta) error
- func (s *Store) PreviousPkScripts(ns walletdb.ReadBucket, rec *TxRecord, block *Block) ([][]byte, error)
- func (s *Store) RangeTransactions(ns walletdb.ReadBucket, begin, end int32, f func([]TxDetails) (bool, error)) error
- func (s *Store) RemoveUnminedTx(ns walletdb.ReadWriteBucket, rec *TxRecord) error
- func (s *Store) Rollback(ns walletdb.ReadWriteBucket, height int32) error
- func (s *Store) TxDetails(ns walletdb.ReadBucket, txHash *chainhash.Hash) (*TxDetails, error)
- func (s *Store) UniqueTxDetails(ns walletdb.ReadBucket, txHash *chainhash.Hash, block *Block) (*TxDetails, error)
- func (s *Store) UnminedTxHashes(ns walletdb.ReadBucket) ([]*chainhash.Hash, error)
- func (s *Store) UnminedTxs(ns walletdb.ReadBucket) ([]*wire.MsgTx, error)
- func (s *Store) UnspentOutputs(ns walletdb.ReadBucket) ([]Credit, error)
- type TxDetails
- type TxRecord
Examples ¶
Constants ¶
const (
// LatestVersion is the most recent store version.
LatestVersion = 1
)
Database versions. Versions start at 1 and increment for each database change.
Variables ¶
var Log = cl.NewSubSystem("chain/tx/mgr", ll.DEFAULT)
Log is the logger for the peer package
Functions ¶
Types ¶
type CreditRecord ¶
type DebitRecord ¶
type Error ¶
type ErrorCode ¶
type ErrorCode uint8
const ( // set to the underlying error returned from the database. ErrDatabase ErrorCode = iota // programming error in this package. ErrData // a credit at an index for which no transaction output exists. ErrInput // continue because a store already exists in the namespace. ErrAlreadyExists // handled by creating a new store. ErrNoExists // database contains an older version of the store. ErrNeedsUpgrade // software. This likely indicates an outdated binary. ErrUnknownVersion )
These constants are used to identify a specific Error.
type Store ¶
type Store struct { // caller. NotifyUnspent func(hash *chainhash.Hash, index uint32) // contains filtered or unexported fields }
func (*Store) AddCredit ¶
func (s *Store) AddCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) error
inserted into the store.
func (*Store) Balance ¶
func (s *Store) Balance(ns walletdb.ReadBucket, minConf int32, syncHeight int32) (util.Amount, error)
height of the most recent mined transaction in the store.
Example ¶
mined transaction given 0, 1, and 6 block confirmations.
s, db, teardown, err := testStore() defer teardown() if err != nil { fmt.Println(err) return } // Prints balances for 0 block confirmations, 1 confirmation, and 6 // confirmations. printBalances := func(syncHeight int32) { dbtx, err := db.BeginReadTx() if err != nil { fmt.Println(err) return } defer dbtx.Rollback() ns := dbtx.ReadBucket(namespaceKey) zeroConfBal, err := s.Balance(ns, 0, syncHeight) if err != nil { fmt.Println(err) return } oneConfBal, err := s.Balance(ns, 1, syncHeight) if err != nil { fmt.Println(err) return } sixConfBal, err := s.Balance(ns, 6, syncHeight) if err != nil { fmt.Println(err) return } fmt.Printf("%v, %v, %v\n", zeroConfBal, oneConfBal, sixConfBal) } // Insert a transaction which outputs 10 DUO unmined and mark the output // as a credit. err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(namespaceKey) err := s.InsertTx(ns, exampleTxRecordA, nil) if err != nil { return err } return s.AddCredit(ns, exampleTxRecordA, nil, 0, false) }) if err != nil { fmt.Println(err) return } printBalances(100) // Mine the transaction in block 100 and print balances again with a // sync height of 100 and 105 blocks. err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(namespaceKey) return s.InsertTx(ns, exampleTxRecordA, &exampleBlock100) }) if err != nil { fmt.Println(err) return } printBalances(100) printBalances(105)
Output:
func (*Store) PreviousPkScripts ¶
func (s *Store) PreviousPkScripts(ns walletdb.ReadBucket, rec *TxRecord, block *Block) ([][]byte, error)
PreviousPkScripts returns a slice of previous output scripts for each credit output this transaction record debits from.
func (*Store) RangeTransactions ¶
func (s *Store) RangeTransactions(ns walletdb.ReadBucket, begin, end int32, f func([]TxDetails) (bool, error)) error
RangeTransactions runs the function f on all transaction details between blocks on the best chain over the height range [begin,end]. The special height -1 may be used to also include unmined transactions. If the end height comes before the begin height, blocks are iterated in reverse order and unmined transactions (if any) are processed first.
The function f may return an error which, if non-nil, is propagated to the caller. Additionally, a boolean return value allows exiting the function early without reading any additional transactions early when true.
All calls to f are guaranteed to be passed a slice with more than zero elements. The slice may be reused for multiple blocks, so it is not safe to use it after the loop iteration it was acquired.
func (*Store) RemoveUnminedTx ¶
func (s *Store) RemoveUnminedTx(ns walletdb.ReadWriteBucket, rec *TxRecord) error
that depend on it.
func (*Store) Rollback ¶
func (s *Store) Rollback(ns walletdb.ReadWriteBucket, height int32) error
each block to the unconfirmed pool.
Example ¶
s, db, teardown, err := testStore() defer teardown() if err != nil { fmt.Println(err) return } err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(namespaceKey) // Insert a transaction which outputs 10 DUO in a block at height 100. err := s.InsertTx(ns, exampleTxRecordA, &exampleBlock100) if err != nil { return err } // Rollback everything from block 100 onwards. err = s.Rollback(ns, 100) if err != nil { return err } // Assert that the transaction is now unmined. details, err := s.TxDetails(ns, &exampleTxRecordA.Hash) if err != nil { return err } if details == nil { return fmt.Errorf("no details found") } fmt.Println(details.Block.Height) return nil }) if err != nil { fmt.Println(err) return }
Output:
func (*Store) TxDetails ¶
TxDetails looks up all recorded details regarding a transaction with some hash. In case of a hash collision, the most recent transaction with a matching hash is returned.
Not finding a transaction with this hash is not an error. In this case, a nil TxDetails is returned.
func (*Store) UniqueTxDetails ¶
func (s *Store) UniqueTxDetails(ns walletdb.ReadBucket, txHash *chainhash.Hash, block *Block) (*TxDetails, error)
UniqueTxDetails looks up all recorded details for a transaction recorded mined in some particular block, or an unmined transaction if block is nil.
Not finding a transaction with this hash from this block is not an error. In this case, a nil TxDetails is returned.
func (*Store) UnminedTxHashes ¶
UnminedTxHashes returns the hashes of all transactions not known to have been mined in a block.
func (*Store) UnminedTxs ¶
UnminedTxs returns the underlying transactions for all unmined transactions which are not known to have been mined in a block. Transactions are guaranteed to be sorted by their dependency order.
func (*Store) UnspentOutputs ¶
func (s *Store) UnspentOutputs(ns walletdb.ReadBucket) ([]Credit, error)
The order is undefined.
type TxDetails ¶
type TxDetails struct { TxRecord Block BlockMeta Credits []CreditRecord Debits []DebitRecord }