// rangeUnminedTransactions executes the function f with TxDetails for every // unmined transaction. f is not executed if no unmined transactions exist. // Error returns from f (if any) are propigated to the caller. Returns true // (signaling breaking out of a RangeTransactions) iff f executes and returns // true. func (s *Store) rangeUnminedTransactions(ns walletdb.Bucket, f func([]TxDetails) (bool, error)) (bool, error) { var details []TxDetails err := ns.Bucket(bucketUnmined).ForEach(func(k, v []byte) error { if len(k) < 32 { str := fmt.Sprintf("%s: short key (expected %d "+ "bytes, read %d)", bucketUnmined, 32, len(k)) return storeError(ErrData, str, nil) } var txHash chainhash.Hash copy(txHash[:], k) detail, err := s.unminedTxDetails(ns, &txHash, v) if err != nil { return err } // Because the key was created while foreach-ing over the // bucket, it should be impossible for unminedTxDetails to ever // successfully return a nil details struct. details = append(details, *detail) return nil }) if err == nil && len(details) > 0 { return f(details) } return false, err }
// testDeleteValues removes all of the provided key/value pairs from the // provided bucket. func testDeleteValues(tc *testContext, bucket walletdb.Bucket, values map[string]string) bool { for k := range values { if err := bucket.Delete([]byte(k)); err != nil { tc.t.Errorf("Delete: unexpected error: %v", err) return false } } return true }
// testPutValues stores all of the provided key/value pairs in the provided // bucket while checking for errors. func testPutValues(tc *testContext, bucket walletdb.Bucket, values map[string]string) bool { for k, v := range values { var vBytes []byte if v != "" { vBytes = []byte(v) } if err := bucket.Put([]byte(k), vBytes); err != nil { tc.t.Errorf("Put: unexpected error: %v", err) return false } } return true }
// testGetValues checks that all of the provided key/value pairs can be // retrieved from the database and the retrieved values match the provided // values. func testGetValues(tc *testContext, bucket walletdb.Bucket, values map[string]string) bool { for k, v := range values { var vBytes []byte if v != "" { vBytes = []byte(v) } gotValue := bucket.Get([]byte(k)) if !reflect.DeepEqual(gotValue, vBytes) { tc.t.Errorf("Get: unexpected value - got %s, want %s", gotValue, vBytes) return false } } return true }
// testBucketInterface ensures the bucket interface is working properly by // exercising all of its functions. func testBucketInterface(tc *testContext, bucket walletdb.Bucket) bool { if bucket.Writable() != tc.isWritable { tc.t.Errorf("Bucket writable state does not match.") return false } if tc.isWritable { // keyValues holds the keys and values to use when putting // values into the bucket. var keyValues = map[string]string{ "bucketkey1": "foo1", "bucketkey2": "foo2", "bucketkey3": "foo3", } if !testPutValues(tc, bucket, keyValues) { return false } if !testGetValues(tc, bucket, keyValues) { return false } // Iterate all of the keys using ForEach while making sure the // stored values are the expected values. keysFound := make(map[string]struct{}, len(keyValues)) err := bucket.ForEach(func(k, v []byte) error { kString := string(k) wantV, ok := keyValues[kString] if !ok { return fmt.Errorf("ForEach: key '%s' should "+ "exist", kString) } if !reflect.DeepEqual(v, []byte(wantV)) { return fmt.Errorf("ForEach: value for key '%s' "+ "does not match - got %s, want %s", kString, v, wantV) } keysFound[kString] = struct{}{} return nil }) if err != nil { tc.t.Errorf("%v", err) return false } // Ensure all keys were iterated. for k := range keyValues { if _, ok := keysFound[k]; !ok { tc.t.Errorf("ForEach: key '%s' was not iterated "+ "when it should have been", k) return false } } // Delete the keys and ensure they were deleted. if !testDeleteValues(tc, bucket, keyValues) { return false } if !testGetValues(tc, bucket, rollbackValues(keyValues)) { return false } // Ensure creating a new bucket works as expected. testBucketName := []byte("testbucket") testBucket, err := bucket.CreateBucket(testBucketName) if err != nil { tc.t.Errorf("CreateBucket: unexpected error: %v", err) return false } if !testNestedBucket(tc, testBucket) { return false } // Ensure creating a bucket that already exists fails with the // expected error. wantErr := walletdb.ErrBucketExists if _, err := bucket.CreateBucket(testBucketName); err != wantErr { tc.t.Errorf("CreateBucket: unexpected error - got %v, "+ "want %v", err, wantErr) return false } // Ensure CreateBucketIfNotExists returns an existing bucket. testBucket, err = bucket.CreateBucketIfNotExists(testBucketName) if err != nil { tc.t.Errorf("CreateBucketIfNotExists: unexpected "+ "error: %v", err) return false } if !testNestedBucket(tc, testBucket) { return false } // Ensure retrieving and existing bucket works as expected. testBucket = bucket.Bucket(testBucketName) if !testNestedBucket(tc, testBucket) { return false } // Ensure deleting a bucket works as intended. if err := bucket.DeleteBucket(testBucketName); err != nil { tc.t.Errorf("DeleteBucket: unexpected error: %v", err) return false } if b := bucket.Bucket(testBucketName); b != nil { tc.t.Errorf("DeleteBucket: bucket '%s' still exists", testBucketName) return false } // Ensure deleting a bucket that doesn't exist returns the // expected error. wantErr = walletdb.ErrBucketNotFound if err := bucket.DeleteBucket(testBucketName); err != wantErr { tc.t.Errorf("DeleteBucket: unexpected error - got %v, "+ "want %v", err, wantErr) return false } // Ensure CreateBucketIfNotExists creates a new bucket when // it doesn't already exist. testBucket, err = bucket.CreateBucketIfNotExists(testBucketName) if err != nil { tc.t.Errorf("CreateBucketIfNotExists: unexpected "+ "error: %v", err) return false } if !testNestedBucket(tc, testBucket) { return false } // Delete the test bucket to avoid leaving it around for future // calls. if err := bucket.DeleteBucket(testBucketName); err != nil { tc.t.Errorf("DeleteBucket: unexpected error: %v", err) return false } if b := bucket.Bucket(testBucketName); b != nil { tc.t.Errorf("DeleteBucket: bucket '%s' still exists", testBucketName) return false } } else { // Put should fail with bucket that is not writable. wantErr := walletdb.ErrTxNotWritable failBytes := []byte("fail") if err := bucket.Put(failBytes, failBytes); err != wantErr { tc.t.Errorf("Put did not fail with unwritable bucket") return false } // Delete should fail with bucket that is not writable. if err := bucket.Delete(failBytes); err != wantErr { tc.t.Errorf("Put did not fail with unwritable bucket") return false } // CreateBucket should fail with bucket that is not writable. if _, err := bucket.CreateBucket(failBytes); err != wantErr { tc.t.Errorf("CreateBucket did not fail with unwritable " + "bucket") return false } // CreateBucketIfNotExists should fail with bucket that is not // writable. if _, err := bucket.CreateBucketIfNotExists(failBytes); err != wantErr { tc.t.Errorf("CreateBucketIfNotExists did not fail with " + "unwritable bucket") return false } // DeleteBucket should fail with bucket that is not writable. if err := bucket.DeleteBucket(failBytes); err != wantErr { tc.t.Errorf("DeleteBucket did not fail with unwritable " + "bucket") return false } } return true }
func (s *Store) unminedTxs(ns walletdb.Bucket) ([]*wire.MsgTx, error) { var unmined []*TxRecord err := ns.Bucket(bucketUnmined).ForEach(func(k, v []byte) error { // TODO: Parsing transactions from the db may be a little // expensive. It's possible the caller only wants the // serialized transactions. var txHash chainhash.Hash err := readRawUnminedHash(k, &txHash) if err != nil { return err } var rec TxRecord err = readRawTxRecord(&txHash, v, &rec) if err != nil { return err } unmined = append(unmined, &rec) return nil }) // Sort by dependency on other transactions, if any. g, i, err := parseTxRecsAsGraph(unmined) if err != nil { return nil, err } order, _, err := topSortKahn(g, i) if err != nil { return nil, err } // Transactions with no local depencies are excluded from this list, so // we need to add them back now. First, find transactions with local // dependencies. Then, sort those as DAGs. Finally, append all the // transactions with no local dependencies and ship them out to the // caller. numTxs := len(unmined) numOrder := len(order) allTxs := make([]*TxRecord, numTxs, numTxs) orderTxs := make([]*TxRecord, numOrder, numOrder) if order != nil { for idx, tx := range order { allTxs[idx] = txRecFromSliceByHash(unmined, tx) orderTxs[idx] = txRecFromSliceByHash(unmined, tx) } } else { orderTxs = nil } itr := len(order) for _, tx := range unmined { if !txRecExistsInSlice(orderTxs, tx) { allTxs[itr] = tx itr++ } } txs := make([]*wire.MsgTx, numTxs, numTxs) for i, txr := range allTxs { txs[i] = &txr.MsgTx } return txs, err }
func (s *Store) debugBucketUnspentString(ns walletdb.Bucket, inclUnmined bool) (string, error) { var unspent []*unspentDebugData var op wire.OutPoint var block Block err := ns.Bucket(bucketUnspent).ForEach(func(k, v []byte) error { err := readCanonicalOutPoint(k, &op) if err != nil { return err } existsUnmined := false if existsRawUnminedInput(ns, k) != nil { // Skip including unmined if specified. if !inclUnmined { return nil } existsUnmined = true } err = readUnspentBlock(v, &block) if err != nil { return err } thisUnspentOutput := &unspentDebugData{ op, existsUnmined, block.Hash, block.Height, } unspent = append(unspent, thisUnspentOutput) return nil }) if err != nil { if _, ok := err.(Error); ok { return "", err } str := "failed iterating unspent bucket" return "", storeError(ErrDatabase, str, err) } sort.Sort(ByOutpoint(unspent)) var buffer bytes.Buffer str := fmt.Sprintf("Unspent outputs\n\n") buffer.WriteString(str) // Create a buffer, dump all the data into it, and hash. var thumbprintBuf bytes.Buffer for _, udd := range unspent { str = fmt.Sprintf("Hash: %v, Index: %v, Tree: %v, Unmined: %v, "+ "Block: %v, Block height: %v\n", udd.outPoint.Hash, udd.outPoint.Index, udd.outPoint.Tree, udd.unmined, udd.block, udd.blockHeight) buffer.WriteString(str) writeUnspentDebugDataToBuf(&thumbprintBuf, udd) } unspentHash := chainhash.HashFunc(thumbprintBuf.Bytes()) unspentThumbprint, err := chainhash.NewHash(unspentHash[:]) if err != nil { return "", err } str = fmt.Sprintf("\nUnspent outputs thumbprint: %v", unspentThumbprint) buffer.WriteString(str) return buffer.String(), nil }