Example #1
0
// putMeta puts a k-v into the meta bucket.
func putMeta(ns walletdb.ReadWriteBucket, key []byte, n int32) error {
	bucket := ns.NestedReadWriteBucket(metaBucketName)
	err := bucket.Put(key, uint32ToBytes(uint32(n)))
	if err != nil {
		str := fmt.Sprintf("failed to store meta key '%s'", key)
		return stakeStoreError(ErrDatabase, str, err)
	}
	return nil
}
Example #2
0
// testDeleteValues removes all of the provided key/value pairs from the
// provided bucket.
func testDeleteValues(tc *testContext, bucket walletdb.ReadWriteBucket, 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
}
Example #3
0
// testPutValues stores all of the provided key/value pairs in the provided
// bucket while checking for errors.
func testPutValues(tc *testContext, bucket walletdb.ReadWriteBucket, 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
}
Example #4
0
// updateStakePoolUserTickets updates a database entry for a pool user's tickets.
// The function pulls the current entry in the database, checks to see if the
// ticket is already there, updates it accordingly, or adds it to the list of
// tickets.
func updateStakePoolUserTickets(ns walletdb.ReadWriteBucket, scriptHash [20]byte,
	record *PoolTicket) error {

	// Fetch the current content of the key.
	// Possible buggy behaviour: If deserialization fails,
	// we won't detect it here. We assume we're throwing
	// ErrPoolUserTicketsNotFound.
	oldRecords, _ := fetchStakePoolUserTickets(ns, scriptHash)

	// Don't reinsert duplicate records we already have.
	if duplicateExistsInUserTickets(record, oldRecords) {
		return nil
	}

	// Does this modify an old record? If so, modify the record
	// itself and push. Otherwise, we need to insert a new
	// record.
	var records []*PoolTicket
	preExists, loc := recordExistsInUserTickets(record, oldRecords)
	if preExists {
		records = oldRecords
		records[loc] = record
	} else {
		// Either create a slice if currently nothing exists for this
		// key in the db, or append the entry to the slice.
		if oldRecords == nil {
			records = make([]*PoolTicket, 1)
			records[0] = record
		} else {
			records = append(oldRecords, record)
		}
	}

	bucket := ns.NestedReadWriteBucket(metaBucketName)
	key := make([]byte, stakePoolTicketsPrefixSize+scriptHashSize)
	copy(key[0:stakePoolTicketsPrefixSize], stakePoolTicketsPrefix)
	copy(key[stakePoolTicketsPrefixSize:stakePoolTicketsPrefixSize+scriptHashSize],
		scriptHash[:])

	// Write the serialized ticket data keyed by the script.
	serializedRecords := serializeUserTickets(records)

	err := bucket.Put(key, serializedRecords)
	if err != nil {
		str := fmt.Sprintf("failed to store pool user ticket records '%x'",
			scriptHash)
		return stakeStoreError(ErrDatabase, str, err)
	}
	return nil
}
Example #5
0
// updateSStxRecord updates a sstx record in the sstx records bucket.
func updateSStxRecord(ns walletdb.ReadWriteBucket, record *sstxRecord, voteBits stake.VoteBits) error {
	bucket := ns.NestedReadWriteBucket(sstxRecordsBucketName)

	// Write the serialized txrecord keyed by the tx hash.
	serializedSStxRecord, err := serializeSStxRecord(record, voteBits)
	if err != nil {
		str := fmt.Sprintf("failed to serialize sstxrecord '%s'", record.tx.Sha())
		return stakeStoreError(ErrDatabase, str, err)
	}
	err = bucket.Put(record.tx.Sha().Bytes(), serializedSStxRecord)
	if err != nil {
		str := fmt.Sprintf("failed to store sstxrecord '%s'", record.tx.Sha())
		return stakeStoreError(ErrDatabase, str, err)
	}
	return nil
}
Example #6
0
// 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.ReadWriteBucket, 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
}
Example #7
0
// updateSStxRecordVoteBits updates an individual ticket's intended voteBits
// which are used to override the default voteBits when voting.
func updateSStxRecordVoteBits(ns walletdb.ReadWriteBucket, hash *chainhash.Hash,
	voteBits stake.VoteBits) error {
	if len(voteBits.ExtendedBits) > stake.MaxSingleBytePushLength-2 {
		str := fmt.Sprintf("voteBitsExt too long (got %v bytes, want max %v)",
			len(voteBits.ExtendedBits), stake.MaxSingleBytePushLength-2)
		return stakeStoreError(ErrData, str, nil)
	}

	bucket := ns.NestedReadWriteBucket(sstxRecordsBucketName)

	key := hash.Bytes()
	val := bucket.Get(key)
	if val == nil {
		str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
		return stakeStoreError(ErrSStxNotFound, str, nil)
	}
	valLen := len(val)
	valCopy := make([]byte, valLen, valLen)
	copy(valCopy, val)

	// Move the cursor to the voteBits position and rewrite it.
	curPos := 0
	curPos += int64Size
	curPos += int32Size

	// Write the intended votebits length (uint8).
	valCopy[curPos] = byte(int16Size + len(voteBits.ExtendedBits))
	curPos += int8Size

	// Write the first two bytes for the intended votebits.
	byteOrder.PutUint16(valCopy[curPos:curPos+int16Size], voteBits.Bits)
	curPos += int16Size

	// Copy the remaining data from voteBitsExt.
	copy(valCopy[curPos:], voteBits.ExtendedBits[:])

	err := bucket.Put(key, valCopy)
	if err != nil {
		str := fmt.Sprintf("failed to update sstxrecord votebits for '%s'", hash)
		return stakeStoreError(ErrDatabase, str, err)
	}
	return nil
}
Example #8
0
// removeStakePoolInvalUserTickets removes the ticket hash from the inval
// ticket bucket.
func removeStakePoolInvalUserTickets(ns walletdb.ReadWriteBucket, scriptHash [20]byte,
	record *chainhash.Hash) error {

	// Fetch the current content of the key.
	// Possible buggy behaviour: If deserialization fails,
	// we won't detect it here. We assume we're throwing
	// ErrPoolUserInvalTcktsNotFound.
	oldRecords, _ := fetchStakePoolUserInvalTickets(ns, scriptHash)

	// Don't need to remove records that don't exist.
	if !duplicateExistsInInvalTickets(record, oldRecords) {
		return nil
	}

	var newRecords []*chainhash.Hash
	for i := range oldRecords {
		if record.IsEqual(oldRecords[i]) {
			newRecords = append(oldRecords[:i:i], oldRecords[i+1:]...)
		}
	}

	if newRecords == nil {
		return nil
	}

	bucket := ns.NestedReadWriteBucket(metaBucketName)
	key := make([]byte, stakePoolInvalidPrefixSize+scriptHashSize)
	copy(key[0:stakePoolInvalidPrefixSize], stakePoolInvalidPrefix)
	copy(key[stakePoolInvalidPrefixSize:stakePoolInvalidPrefixSize+scriptHashSize],
		scriptHash[:])

	// Write the serialized invalid user ticket hashes.
	serializedRecords := serializeUserInvalTickets(newRecords)

	err := bucket.Put(key, serializedRecords)
	if err != nil {
		str := fmt.Sprintf("failed to store pool user invalid ticket "+
			"records '%x'", scriptHash)
		return stakeStoreError(ErrDatabase, str, err)
	}

	return nil
}
Example #9
0
// updateStakePoolInvalUserTickets updates a database entry for a pool user's
// invalid tickets. The function pulls the current entry in the database,
// checks to see if the ticket is already there. If it is it returns, otherwise
// it adds it to the list of tickets.
func updateStakePoolInvalUserTickets(ns walletdb.ReadWriteBucket, scriptHash [20]byte,
	record *chainhash.Hash) error {

	// Fetch the current content of the key.
	// Possible buggy behaviour: If deserialization fails,
	// we won't detect it here. We assume we're throwing
	// ErrPoolUserInvalTcktsNotFound.
	oldRecords, _ := fetchStakePoolUserInvalTickets(ns, scriptHash)

	// Don't reinsert duplicate records we already have.
	if duplicateExistsInInvalTickets(record, oldRecords) {
		return nil
	}

	// Either create a slice if currently nothing exists for this
	// key in the db, or append the entry to the slice.
	var records []*chainhash.Hash
	if oldRecords == nil {
		records = make([]*chainhash.Hash, 1)
		records[0] = record
	} else {
		records = append(oldRecords, record)
	}

	bucket := ns.NestedReadWriteBucket(metaBucketName)
	key := make([]byte, stakePoolInvalidPrefixSize+scriptHashSize)
	copy(key[0:stakePoolInvalidPrefixSize], stakePoolInvalidPrefix)
	copy(key[stakePoolInvalidPrefixSize:stakePoolInvalidPrefixSize+scriptHashSize],
		scriptHash[:])

	// Write the serialized invalid user ticket hashes.
	serializedRecords := serializeUserInvalTickets(records)

	err := bucket.Put(key, serializedRecords)
	if err != nil {
		str := fmt.Sprintf("failed to store pool user invalid ticket "+
			"records '%x'", scriptHash)
		return stakeStoreError(ErrDatabase, str, err)
	}
	return nil
}
Example #10
0
// updateSSRtxRecord updates an SSRtx record in the SSRtx records bucket.
func updateSSRtxRecord(ns walletdb.ReadWriteBucket, hash *chainhash.Hash,
	record *ssrtxRecord) error {

	// Fetch the current content of the key.
	// Possible buggy behaviour: If deserialization fails,
	// we won't detect it here. We assume we're throwing
	// ErrSSRtxsNotFound.
	oldRecords, _ := fetchSSRtxRecords(ns, hash)

	// Don't reinsert records we already have.
	if ssrtxRecordExistsInRecords(record, oldRecords) {
		return nil
	}

	bucket := ns.NestedReadWriteBucket(ssrtxRecordsBucketName)

	var records []*ssrtxRecord
	// Either create a slice if currently nothing exists for this
	// key in the db, or append the entry to the slice.
	if oldRecords == nil {
		records = make([]*ssrtxRecord, 1)
		records[0] = record
	} else {
		records = append(oldRecords, record)
	}

	// Write the serialized SSRtxs keyed by the sstx hash.
	serializedSSRtxsRecords := serializeSSRtxRecords(records)

	err := bucket.Put(hash.Bytes(), serializedSSRtxsRecords)
	if err != nil {
		str := fmt.Sprintf("failed to store ssrtx records '%s'", hash)
		return stakeStoreError(ErrDatabase, str, err)
	}
	return nil
}
Example #11
0
// testBucketInterface ensures the bucket interface is working properly by
// exercising all of its functions.
func testBucketInterface(tc *testContext, bucket walletdb.ReadWriteBucket) 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.ReadWriteBucket(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.ReadWriteBucket(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.ReadWriteBucket(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
}
Example #12
0
// initialize creates the DB if it doesn't exist, and otherwise
// loads the database.
func initializeEmpty(ns walletdb.ReadWriteBucket) error {
	// Initialize the buckets and main db fields as needed.
	mainBucket, err := ns.CreateBucketIfNotExists(mainBucketName)
	if err != nil {
		str := "failed to create main bucket"
		return stakeStoreError(ErrDatabase, str, err)
	}

	_, err = ns.CreateBucketIfNotExists(sstxRecordsBucketName)
	if err != nil {
		str := "failed to create sstx records bucket"
		return stakeStoreError(ErrDatabase, str, err)
	}

	_, err = ns.CreateBucketIfNotExists(ssgenRecordsBucketName)
	if err != nil {
		str := "failed to create ssgen records bucket"
		return stakeStoreError(ErrDatabase, str, err)
	}

	_, err = ns.CreateBucketIfNotExists(ssrtxRecordsBucketName)
	if err != nil {
		str := "failed to create ssrtx records bucket"
		return stakeStoreError(ErrDatabase, str, err)
	}

	_, err = ns.CreateBucketIfNotExists(metaBucketName)
	if err != nil {
		str := "failed to create meta bucket"
		return stakeStoreError(ErrDatabase, str, err)
	}

	// Save the most recent tx store version if it isn't already
	// there, otherwise keep track of it for potential upgrades.
	var version uint32
	verBytes := mainBucket.Get(stakeStoreVersionName)
	if verBytes == nil {
		version = LatestStakeMgrVersion

		var buf [4]byte
		byteOrder.PutUint32(buf[:], version)
		err := mainBucket.Put(stakeStoreVersionName, buf[:])
		if err != nil {
			str := "failed to store latest database version"
			return stakeStoreError(ErrDatabase, str, err)
		}
	} else {
		version = byteOrder.Uint32(verBytes)
	}

	var createDate uint64
	createBytes := mainBucket.Get(stakeStoreCreateDateName)
	if createBytes == nil {
		createDate = uint64(time.Now().Unix())
		var buf [8]byte
		byteOrder.PutUint64(buf[:], createDate)
		err := mainBucket.Put(stakeStoreCreateDateName, buf[:])
		if err != nil {
			str := "failed to store database creation time"
			return stakeStoreError(ErrDatabase, str, err)
		}
	} else {
		createDate = byteOrder.Uint64(createBytes)
	}

	if err != nil {
		str := "failed to load database"
		return stakeStoreError(ErrDatabase, str, err)
	}

	return nil
}