// updateSSRtxRecord updates an SSRtx record in the SSRtx records bucket. func updateSSRtxRecord(tx walletdb.Tx, 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(tx, hash) // Don't reinsert records we already have. if ssrtxRecordExistsInRecords(record, oldRecords) { return nil } bucket := tx.RootBucket().Bucket(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 }
// getMaxUsedIdx returns the highest used index from the used addresses bucket // of the given pool, series and branch. func getMaxUsedIdx(tx walletdb.Tx, poolID []byte, seriesID uint32, branch Branch) (Index, error) { maxIdx := Index(0) usedAddrs := tx.RootBucket().Bucket(poolID).Bucket(usedAddrsBucketName) bucket := usedAddrs.Bucket(getUsedAddrBucketID(seriesID, branch)) if bucket == nil { return maxIdx, nil } // FIXME: This is far from optimal and should be optimized either by storing // a separate key in the DB with the highest used idx for every // series/branch or perhaps by doing a large gap linear forward search + // binary backwards search (e.g. check for 1000000, 2000000, .... until it // doesn't exist, and then use a binary search to find the max using the // discovered bounds). err := bucket.ForEach( func(k, v []byte) error { idx := Index(bytesToUint32(k)) if idx > maxIdx { maxIdx = idx } return nil }) if err != nil { return Index(0), newError(ErrDatabase, "failed to get highest idx of used addresses", err) } return maxIdx, nil }
// putMeta func putMeta(tx walletdb.Tx, key []byte, n int32) error { bucket := tx.RootBucket().Bucket(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 }
// getUsedAddrHash returns the addr hash with the given index from the used // addresses bucket of the given pool, series and branch. func getUsedAddrHash(tx walletdb.Tx, poolID []byte, seriesID uint32, branch Branch, index Index) []byte { usedAddrs := tx.RootBucket().Bucket(poolID).Bucket(usedAddrsBucketName) bucket := usedAddrs.Bucket(getUsedAddrBucketID(seriesID, branch)) if bucket == nil { return nil } return bucket.Get(uint32ToBytes(uint32(index))) }
// putUsedAddrHash adds an entry (key==index, value==encryptedHash) to the used // addresses bucket of the given pool, series and branch. func putUsedAddrHash(tx walletdb.Tx, poolID []byte, seriesID uint32, branch Branch, index Index, encryptedHash []byte) error { usedAddrs := tx.RootBucket().Bucket(poolID).Bucket(usedAddrsBucketName) bucket, err := usedAddrs.CreateBucketIfNotExists(getUsedAddrBucketID(seriesID, branch)) if err != nil { return newError(ErrDatabase, "failed to store used address hash", err) } return bucket.Put(uint32ToBytes(uint32(index)), encryptedHash) }
// fetchSStxRecord retrieves a tx record from the sstx records bucket // with the given hash. func fetchSStxRecord(tx walletdb.Tx, hash *chainhash.Hash) (*sstxRecord, error) { bucket := tx.RootBucket().Bucket(sstxRecordsBucketName) key := hash.Bytes() val := bucket.Get(key) if val == nil { str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String()) return nil, stakeStoreError(ErrSStxNotFound, str, nil) } return deserializeSStxRecord(val) }
// fetchMeta func fetchMeta(tx walletdb.Tx, key []byte) (int32, error) { bucket := tx.RootBucket().Bucket(metaBucketName) val := bucket.Get(key) // Return 0 if the metadata is uninitialized if val == nil { return 0, nil } if val == nil { str := fmt.Sprintf("meta key not found %s", key) return 0, stakeStoreError(ErrDatabase, str, nil) } return int32(byteOrder.Uint32(val)), nil }
// updateSStxRecord updates a sstx record in the sstx records bucket. func updateSStxRecord(tx walletdb.Tx, record *sstxRecord) error { bucket := tx.RootBucket().Bucket(sstxRecordsBucketName) // Write the serialized txrecord keyed by the tx hash. serializedSStxRecord, err := serializeSStxRecord(record) 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 }
// putSeriesRow stores the given series row inside a voting pool bucket named // after poolID. The voting pool bucket does not need to be created // beforehand. func putSeriesRow(tx walletdb.Tx, poolID []byte, ID uint32, row *dbSeriesRow) error { bucket, err := tx.RootBucket().CreateBucketIfNotExists(poolID) if err != nil { str := fmt.Sprintf("cannot create bucket %v", poolID) return newError(ErrDatabase, str, err) } bucket = bucket.Bucket(seriesBucketName) serialized, err := serializeSeriesRow(row) if err != nil { return err } err = bucket.Put(uint32ToBytes(ID), serialized) if err != nil { str := fmt.Sprintf("cannot put series %v into bucket %v", serialized, poolID) return newError(ErrDatabase, str, err) } return nil }
// loadAllSeries returns a map of all the series stored inside a voting pool // bucket, keyed by id. func loadAllSeries(tx walletdb.Tx, poolID []byte) (map[uint32]*dbSeriesRow, error) { bucket := tx.RootBucket().Bucket(poolID).Bucket(seriesBucketName) allSeries := make(map[uint32]*dbSeriesRow) err := bucket.ForEach( func(k, v []byte) error { seriesID := bytesToUint32(k) series, err := deserializeSeriesRow(v) if err != nil { return err } allSeries[seriesID] = series return nil }) if err != nil { return nil, err } return allSeries, nil }
// putPool stores a voting pool in the database, creating a bucket named // after the voting pool id and two other buckets inside it to store series and // used addresses for that pool. func putPool(tx walletdb.Tx, poolID []byte) error { poolBucket, err := tx.RootBucket().CreateBucket(poolID) if err != nil { return newError(ErrDatabase, fmt.Sprintf("cannot create pool %v", poolID), err) } _, err = poolBucket.CreateBucket(seriesBucketName) if err != nil { return newError(ErrDatabase, fmt.Sprintf("cannot create series bucket for pool %v", poolID), err) } _, err = poolBucket.CreateBucket(usedAddrsBucketName) if err != nil { return newError(ErrDatabase, fmt.Sprintf("cannot create used addrs bucket for pool %v", poolID), err) } _, err = poolBucket.CreateBucket(withdrawalsBucketName) if err != nil { return newError( ErrDatabase, fmt.Sprintf("cannot create withdrawals bucket for pool %v", poolID), err) } return nil }
func getWithdrawal(tx walletdb.Tx, poolID []byte, roundID uint32) []byte { bucket := tx.RootBucket().Bucket(poolID) return bucket.Get(uint32ToBytes(roundID)) }
func putWithdrawal(tx walletdb.Tx, poolID []byte, roundID uint32, serialized []byte) error { bucket := tx.RootBucket().Bucket(poolID) return bucket.Put(uint32ToBytes(roundID), serialized) }
// existsPool checks the existence of a bucket named after the given // voting pool id. func existsPool(tx walletdb.Tx, poolID []byte) bool { bucket := tx.RootBucket().Bucket(poolID) return bucket != nil }