waddrmgr: introduce new bucket hierarchy with scopes at the top
In this commit, we make a fundamental modification bucket structure within the database. Most buckets are no under an additional layer of nesting: the scope. The scope encapsulates which (purpose, coin type) pair the address, accounts, and coin type keys belong to.
This commit is contained in:
parent
8bd9f7713b
commit
e8755c2bc2
1 changed files with 488 additions and 137 deletions
625
waddrmgr/db.go
625
waddrmgr/db.go
|
@ -139,43 +139,98 @@ var (
|
||||||
nullVal = []byte{0}
|
nullVal = []byte{0}
|
||||||
|
|
||||||
// Bucket names.
|
// Bucket names.
|
||||||
|
|
||||||
|
// scopeSchemaBucket is the name of the bucket that maps a particular
|
||||||
|
// manager scope to the type of addresses that should be derived for
|
||||||
|
// particular branches during key derivation.
|
||||||
|
scopeSchemaBucketName = []byte("scope-schema")
|
||||||
|
|
||||||
|
// scopeBucketNme is the name of the top-level bucket within the
|
||||||
|
// hierarchy. It maps: purpose || coinType to a new sub-bucket that
|
||||||
|
// will house a scoped address manager. All buckets below are a child
|
||||||
|
// of this bucket:
|
||||||
|
//
|
||||||
|
// scopeBucket -> scope -> acctBucket
|
||||||
|
// scopeBucket -> scope -> addrBucket
|
||||||
|
// scopeBucket -> scope -> usedAddrBucket
|
||||||
|
// scopeBucket -> scope -> addrAcctIdxBucket
|
||||||
|
// scopeBucket -> scope -> acctNameIdxBucket
|
||||||
|
// scopeBucket -> scope -> acctIDIdxBucketName
|
||||||
|
// scopeBucket -> scope -> metaBucket
|
||||||
|
// scopeBucket -> scope -> metaBucket -> lastAccountNameKey
|
||||||
|
// scopeBucket -> scope -> coinTypePrivKey
|
||||||
|
// scopeBucket -> scope -> coinTypePubKey
|
||||||
|
scopeBucketName = []byte("scope")
|
||||||
|
|
||||||
|
// coinTypePrivKeyName is the name of the key within a particular scope
|
||||||
|
// bucket that stores the encrypted cointype private keys. Each scope
|
||||||
|
// within the database will have its own set of coin type keys.
|
||||||
|
coinTypePrivKeyName = []byte("ctpriv")
|
||||||
|
|
||||||
|
// coinTypePrivKeyName is the name of the key within a particular scope
|
||||||
|
// bucket that stores the encrypted cointype public keys. Each scope
|
||||||
|
// will have its own set of coin type public keys.
|
||||||
|
coinTypePubKeyName = []byte("ctpub")
|
||||||
|
|
||||||
|
// acctBucketName is the bucket directly below the scope bucket in the
|
||||||
|
// hierarchy. This bucket stores all the information and indexes
|
||||||
|
// relevant to an account.
|
||||||
acctBucketName = []byte("acct")
|
acctBucketName = []byte("acct")
|
||||||
|
|
||||||
|
// addrBucketName is the name of the bucket that stores a mapping of
|
||||||
|
// pubkey hash to address type. This will be used to quickly determine
|
||||||
|
// if a given address is under our control.
|
||||||
addrBucketName = []byte("addr")
|
addrBucketName = []byte("addr")
|
||||||
|
|
||||||
// addrAcctIdxBucketName is used to index account addresses
|
// addrAcctIdxBucketName is used to index account addresses Entries in
|
||||||
// Entries in this index may map:
|
// this index may map:
|
||||||
// * addr hash => account id
|
// * addr hash => account id
|
||||||
// * account bucket -> addr hash => null
|
// * account bucket -> addr hash => null
|
||||||
// To fetch the account of an address, lookup the value using
|
//
|
||||||
// the address hash.
|
// To fetch the account of an address, lookup the value using the
|
||||||
// To fetch all addresses of an account, fetch the account bucket, iterate
|
// address hash.
|
||||||
// over the keys and fetch the address row from the addr bucket.
|
//
|
||||||
|
// To fetch all addresses of an account, fetch the account bucket,
|
||||||
|
// iterate over the keys and fetch the address row from the addr
|
||||||
|
// bucket.
|
||||||
|
//
|
||||||
// The index needs to be updated whenever an address is created e.g.
|
// The index needs to be updated whenever an address is created e.g.
|
||||||
// NewAddress
|
// NewAddress
|
||||||
addrAcctIdxBucketName = []byte("addracctidx")
|
addrAcctIdxBucketName = []byte("addracctidx")
|
||||||
|
|
||||||
// acctNameIdxBucketName is used to create an index
|
// acctNameIdxBucketName is used to create an index mapping an account
|
||||||
// mapping an account name string to the corresponding
|
// name string to the corresponding account id. The index needs to be
|
||||||
// account id.
|
// updated whenever the account name and id changes e.g. RenameAccount
|
||||||
// The index needs to be updated whenever the account name
|
//
|
||||||
// and id changes e.g. RenameAccount
|
// string => account_id
|
||||||
acctNameIdxBucketName = []byte("acctnameidx")
|
acctNameIdxBucketName = []byte("acctnameidx")
|
||||||
|
|
||||||
// acctIDIdxBucketName is used to create an index
|
// acctIDIdxBucketName is used to create an index mapping an account id
|
||||||
// mapping an account id to the corresponding
|
// to the corresponding account name string. The index needs to be
|
||||||
// account name string.
|
// updated whenever the account name and id changes e.g. RenameAccount
|
||||||
// The index needs to be updated whenever the account name
|
//
|
||||||
// and id changes e.g. RenameAccount
|
// account_id => string
|
||||||
acctIDIdxBucketName = []byte("acctididx")
|
acctIDIdxBucketName = []byte("acctididx")
|
||||||
|
|
||||||
|
// usedAddrBucketName is the name of the bucket that stores an
|
||||||
|
// addresses hash if the address has been used or not.
|
||||||
|
usedAddrBucketName = []byte("usedaddrs")
|
||||||
|
|
||||||
// meta is used to store meta-data about the address manager
|
// meta is used to store meta-data about the address manager
|
||||||
// e.g. last account number
|
// e.g. last account number
|
||||||
metaBucketName = []byte("meta")
|
metaBucketName = []byte("meta")
|
||||||
|
|
||||||
// lastAccountName is used to store the metadata - last account
|
// lastAccountName is used to store the metadata - last account
|
||||||
// in the manager
|
// in the manager
|
||||||
lastAccountName = []byte("lastaccount")
|
lastAccountName = []byte("lastaccount")
|
||||||
|
|
||||||
|
// mainBucketName is the name of the bucket that stores the encrypted
|
||||||
|
// crypto keys that encrypt all other generated keys, the watch only
|
||||||
|
// flag, the master private key (encrypted), the master HD private key
|
||||||
|
// (encrypted), and also versioning information.
|
||||||
mainBucketName = []byte("main")
|
mainBucketName = []byte("main")
|
||||||
|
// syncBucketName is the name of the bucket that stores the current
|
||||||
|
// sync state of the root manager.
|
||||||
syncBucketName = []byte("sync")
|
syncBucketName = []byte("sync")
|
||||||
|
|
||||||
// Db related key names (main bucket).
|
// Db related key names (main bucket).
|
||||||
|
@ -188,20 +243,12 @@ var (
|
||||||
cryptoPrivKeyName = []byte("cpriv")
|
cryptoPrivKeyName = []byte("cpriv")
|
||||||
cryptoPubKeyName = []byte("cpub")
|
cryptoPubKeyName = []byte("cpub")
|
||||||
cryptoScriptKeyName = []byte("cscript")
|
cryptoScriptKeyName = []byte("cscript")
|
||||||
coinTypePrivKeyName = []byte("ctpriv")
|
|
||||||
coinTypePubKeyName = []byte("ctpub")
|
|
||||||
watchingOnlyName = []byte("watchonly")
|
watchingOnlyName = []byte("watchonly")
|
||||||
|
|
||||||
// Sync related key names (sync bucket).
|
// Sync related key names (sync bucket).
|
||||||
syncedToName = []byte("syncedto")
|
syncedToName = []byte("syncedto")
|
||||||
startBlockName = []byte("startblock")
|
startBlockName = []byte("startblock")
|
||||||
birthdayName = []byte("birthday")
|
birthdayName = []byte("birthday")
|
||||||
|
|
||||||
// Account related key names (account bucket).
|
|
||||||
acctNumAcctsName = []byte("numaccts")
|
|
||||||
|
|
||||||
// Used addresses (used bucket)
|
|
||||||
usedAddrBucketName = []byte("usedaddrs")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// uint32ToBytes converts a 32 bit unsigned integer into a 4-byte slice in
|
// uint32ToBytes converts a 32 bit unsigned integer into a 4-byte slice in
|
||||||
|
@ -234,6 +281,114 @@ func stringToBytes(s string) []byte {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scopeKeySize is the size of a scope as stored within the database.
|
||||||
|
const scopeKeySize = 8
|
||||||
|
|
||||||
|
// scopeToBytes transforms a manager's scope into the form that will be used to
|
||||||
|
// retrieve the bucket that all information for a particular scope is stored
|
||||||
|
// under
|
||||||
|
func scopeToBytes(scope *KeyScope) [scopeKeySize]byte {
|
||||||
|
var scopeBytes [scopeKeySize]byte
|
||||||
|
binary.LittleEndian.PutUint32(scopeBytes[:], scope.Purpose)
|
||||||
|
binary.LittleEndian.PutUint32(scopeBytes[4:], scope.Coin)
|
||||||
|
|
||||||
|
return scopeBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// scopeFromBytes decodes a serializes manager scope into its concrete manager
|
||||||
|
// scope struct.
|
||||||
|
func scopeFromBytes(scopeBytes []byte) KeyScope {
|
||||||
|
return KeyScope{
|
||||||
|
Purpose: binary.LittleEndian.Uint32(scopeBytes[:]),
|
||||||
|
Coin: binary.LittleEndian.Uint32(scopeBytes[4:]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scopeSchemaToBytes encodes the passed scope schema as a set of bytes
|
||||||
|
// suitable for storage within the database.
|
||||||
|
func scopeSchemaToBytes(schema *ScopeAddrSchema) []byte {
|
||||||
|
var schemaBytes [2]byte
|
||||||
|
schemaBytes[0] = byte(schema.InternalAddrType)
|
||||||
|
schemaBytes[1] = byte(schema.ExternalAddrType)
|
||||||
|
|
||||||
|
return schemaBytes[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// scopeSchemaFromBytes decodes a new scope schema instance from the set of
|
||||||
|
// serialized bytes.
|
||||||
|
func scopeSchemaFromBytes(schemaBytes []byte) *ScopeAddrSchema {
|
||||||
|
return &ScopeAddrSchema{
|
||||||
|
InternalAddrType: AddressType(schemaBytes[0]),
|
||||||
|
ExternalAddrType: AddressType(schemaBytes[1]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchScopeAddrSchema will attempt to retrieve the address schema for a
|
||||||
|
// particular manager scope stored within the database. These are used in order
|
||||||
|
// to properly type each address generated by the scope address manager.
|
||||||
|
func fetchScopeAddrSchema(ns walletdb.ReadBucket,
|
||||||
|
scope *KeyScope) (*ScopeAddrSchema, error) {
|
||||||
|
|
||||||
|
schemaBucket := ns.NestedReadBucket(scopeSchemaBucketName)
|
||||||
|
if schemaBucket == nil {
|
||||||
|
str := fmt.Sprintf("unable to find scope schema bucket")
|
||||||
|
return nil, managerError(ErrScopeNotFound, str, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeKey := scopeToBytes(scope)
|
||||||
|
schemaBytes := schemaBucket.Get(scopeKey[:])
|
||||||
|
if schemaBytes == nil {
|
||||||
|
str := fmt.Sprintf("unable to find scope %v", scope)
|
||||||
|
return nil, managerError(ErrScopeNotFound, str, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return scopeSchemaFromBytes(schemaBytes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// putScopeAddrSchema attempts to store the passed addr scehma for the given
|
||||||
|
// manager scope.
|
||||||
|
func putScopeAddrTypes(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
|
schema *ScopeAddrSchema) error {
|
||||||
|
|
||||||
|
scopeSchemaBucket := ns.NestedReadWriteBucket(scopeSchemaBucketName)
|
||||||
|
if scopeSchemaBucket == nil {
|
||||||
|
str := fmt.Sprintf("unable to find scope schema bucket")
|
||||||
|
return managerError(ErrScopeNotFound, str, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeKey := scopeToBytes(scope)
|
||||||
|
schemaBytes := scopeSchemaToBytes(schema)
|
||||||
|
return scopeSchemaBucket.Put(scopeKey[:], schemaBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchReadScopeBucket(ns walletdb.ReadBucket, scope *KeyScope) (walletdb.ReadBucket, error) {
|
||||||
|
rootScopeBucket := ns.NestedReadBucket(scopeBucketName)
|
||||||
|
|
||||||
|
scopeKey := scopeToBytes(scope)
|
||||||
|
scopedBucket := rootScopeBucket.NestedReadBucket(scopeKey[:])
|
||||||
|
if scopedBucket == nil {
|
||||||
|
str := fmt.Sprintf("unable to find scope %v", scope)
|
||||||
|
return nil, managerError(ErrScopeNotFound, str, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return scopedBucket, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchWriteScopeBucket(ns walletdb.ReadWriteBucket,
|
||||||
|
scope *KeyScope) (walletdb.ReadWriteBucket, error) {
|
||||||
|
|
||||||
|
rootScopeBucket := ns.NestedReadWriteBucket(scopeBucketName)
|
||||||
|
|
||||||
|
scopeKey := scopeToBytes(scope)
|
||||||
|
scopedBucket := rootScopeBucket.NestedReadWriteBucket(scopeKey[:])
|
||||||
|
if scopedBucket == nil {
|
||||||
|
str := fmt.Sprintf("unable to find scope %v", scope)
|
||||||
|
return nil, managerError(ErrScopeNotFound, str, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return scopedBucket, nil
|
||||||
|
}
|
||||||
|
|
||||||
// fetchManagerVersion fetches the current manager version from the database.
|
// fetchManagerVersion fetches the current manager version from the database.
|
||||||
func fetchManagerVersion(ns walletdb.ReadBucket) (uint32, error) {
|
func fetchManagerVersion(ns walletdb.ReadBucket) (uint32, error) {
|
||||||
mainBucket := ns.NestedReadBucket(mainBucketName)
|
mainBucket := ns.NestedReadBucket(mainBucketName)
|
||||||
|
@ -312,33 +467,44 @@ func putMasterKeyParams(ns walletdb.ReadWriteBucket, pubParams, privParams []byt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchCoinTypeKeys loads the encrypted cointype keys which are in turn used to
|
// fetchCoinTypeKeys loads the encrypted cointype keys which are in turn used
|
||||||
// derive the extended keys for all accounts.
|
// to derive the extended keys for all accounts. Each cointype key is
|
||||||
func fetchCoinTypeKeys(ns walletdb.ReadBucket) ([]byte, []byte, error) {
|
// associated with a particular manager scoped.
|
||||||
bucket := ns.NestedReadBucket(mainBucketName)
|
func fetchCoinTypeKeys(ns walletdb.ReadBucket, scope *KeyScope) ([]byte, []byte, error) {
|
||||||
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
coinTypePubKeyEnc := bucket.Get(coinTypePubKeyName)
|
coinTypePubKeyEnc := scopedBucket.Get(coinTypePubKeyName)
|
||||||
if coinTypePubKeyEnc == nil {
|
if coinTypePubKeyEnc == nil {
|
||||||
str := "required encrypted cointype public key not stored in database"
|
str := "required encrypted cointype public key not stored in database"
|
||||||
return nil, nil, managerError(ErrDatabase, str, nil)
|
return nil, nil, managerError(ErrDatabase, str, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
coinTypePrivKeyEnc := bucket.Get(coinTypePrivKeyName)
|
coinTypePrivKeyEnc := scopedBucket.Get(coinTypePrivKeyName)
|
||||||
if coinTypePrivKeyEnc == nil {
|
if coinTypePrivKeyEnc == nil {
|
||||||
str := "required encrypted cointype private key not stored in database"
|
str := "required encrypted cointype private key not stored in database"
|
||||||
return nil, nil, managerError(ErrDatabase, str, nil)
|
return nil, nil, managerError(ErrDatabase, str, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return coinTypePubKeyEnc, coinTypePrivKeyEnc, nil
|
return coinTypePubKeyEnc, coinTypePrivKeyEnc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putCoinTypeKeys stores the encrypted cointype keys which are in turn used to
|
// putCoinTypeKeys stores the encrypted cointype keys which are in turn used to
|
||||||
// derive the extended keys for all accounts. Either parameter can be nil in which
|
// derive the extended keys for all accounts. Either parameter can be nil in
|
||||||
// case no value is written for the parameter.
|
// which case no value is written for the parameter. Each cointype key is
|
||||||
func putCoinTypeKeys(ns walletdb.ReadWriteBucket, coinTypePubKeyEnc []byte, coinTypePrivKeyEnc []byte) error {
|
// associated with a particular manager scope.
|
||||||
bucket := ns.NestedReadWriteBucket(mainBucketName)
|
func putCoinTypeKeys(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
|
coinTypePubKeyEnc []byte, coinTypePrivKeyEnc []byte) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if coinTypePubKeyEnc != nil {
|
if coinTypePubKeyEnc != nil {
|
||||||
err := bucket.Put(coinTypePubKeyName, coinTypePubKeyEnc)
|
err := scopedBucket.Put(coinTypePubKeyName, coinTypePubKeyEnc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to store encrypted cointype public key"
|
str := "failed to store encrypted cointype public key"
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
|
@ -346,7 +512,7 @@ func putCoinTypeKeys(ns walletdb.ReadWriteBucket, coinTypePubKeyEnc []byte, coin
|
||||||
}
|
}
|
||||||
|
|
||||||
if coinTypePrivKeyEnc != nil {
|
if coinTypePrivKeyEnc != nil {
|
||||||
err := bucket.Put(coinTypePrivKeyName, coinTypePrivKeyEnc)
|
err := scopedBucket.Put(coinTypePrivKeyName, coinTypePrivKeyEnc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to store encrypted cointype private key"
|
str := "failed to store encrypted cointype private key"
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
|
@ -392,9 +558,11 @@ func fetchCryptoKeys(ns walletdb.ReadBucket) ([]byte, []byte, []byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// putCryptoKeys stores the encrypted crypto keys which are in turn used to
|
// putCryptoKeys stores the encrypted crypto keys which are in turn used to
|
||||||
// protect the extended and imported keys. Either parameter can be nil in which
|
// protect the extended and imported keys. Either parameter can be nil in
|
||||||
// case no value is written for the parameter.
|
// which case no value is written for the parameter.
|
||||||
func putCryptoKeys(ns walletdb.ReadWriteBucket, pubKeyEncrypted, privKeyEncrypted, scriptKeyEncrypted []byte) error {
|
func putCryptoKeys(ns walletdb.ReadWriteBucket, pubKeyEncrypted, privKeyEncrypted,
|
||||||
|
scriptKeyEncrypted []byte) error {
|
||||||
|
|
||||||
bucket := ns.NestedReadWriteBucket(mainBucketName)
|
bucket := ns.NestedReadWriteBucket(mainBucketName)
|
||||||
|
|
||||||
if pubKeyEncrypted != nil {
|
if pubKeyEncrypted != nil {
|
||||||
|
@ -569,12 +737,38 @@ func serializeDefaultAccountRow(encryptedPubKey, encryptedPrivKey []byte,
|
||||||
return rawData
|
return rawData
|
||||||
}
|
}
|
||||||
|
|
||||||
// forEachAccount calls the given function with each account stored in
|
// forEachKeyScope calls the given function for each known manager scope
|
||||||
// the manager, breaking early on error.
|
// within the set of scopes known by the root manager.
|
||||||
func forEachAccount(ns walletdb.ReadBucket, fn func(account uint32) error) error {
|
func forEachKeyScope(ns walletdb.ReadBucket, fn func(KeyScope) error) error {
|
||||||
bucket := ns.NestedReadBucket(acctBucketName)
|
bucket := ns.NestedReadBucket(scopeBucketName)
|
||||||
|
|
||||||
return bucket.ForEach(func(k, v []byte) error {
|
return bucket.ForEach(func(k, v []byte) error {
|
||||||
|
// skip non-bucket
|
||||||
|
if len(k) != 8 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := KeyScope{
|
||||||
|
Purpose: binary.LittleEndian.Uint32(k[:]),
|
||||||
|
Coin: binary.LittleEndian.Uint32(k[4:]),
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(scope)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// forEachAccount calls the given function with each account stored in the
|
||||||
|
// manager, breaking early on error.
|
||||||
|
func forEachAccount(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
|
fn func(account uint32) error) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
acctBucket := scopedBucket.NestedReadBucket(acctBucketName)
|
||||||
|
return acctBucket.ForEach(func(k, v []byte) error {
|
||||||
// Skip buckets.
|
// Skip buckets.
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -583,43 +777,65 @@ func forEachAccount(ns walletdb.ReadBucket, fn func(account uint32) error) error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchLastAccount retreives the last account from the database.
|
// fetchLastAccount retrieves the last account from the database.
|
||||||
func fetchLastAccount(ns walletdb.ReadBucket) (uint32, error) {
|
func fetchLastAccount(ns walletdb.ReadBucket, scope *KeyScope) (uint32, error) {
|
||||||
bucket := ns.NestedReadBucket(metaBucketName)
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
val := bucket.Get(lastAccountName)
|
metaBucket := scopedBucket.NestedReadBucket(metaBucketName)
|
||||||
|
|
||||||
|
val := metaBucket.Get(lastAccountName)
|
||||||
if len(val) != 4 {
|
if len(val) != 4 {
|
||||||
str := fmt.Sprintf("malformed metadata '%s' stored in database",
|
str := fmt.Sprintf("malformed metadata '%s' stored in database",
|
||||||
lastAccountName)
|
lastAccountName)
|
||||||
return 0, managerError(ErrDatabase, str, nil)
|
return 0, managerError(ErrDatabase, str, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
account := binary.LittleEndian.Uint32(val[0:4])
|
account := binary.LittleEndian.Uint32(val[0:4])
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchAccountName retreives the account name given an account number from
|
// fetchAccountName retrieves the account name given an account number from the
|
||||||
// the database.
|
// database.
|
||||||
func fetchAccountName(ns walletdb.ReadBucket, account uint32) (string, error) {
|
func fetchAccountName(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadBucket(acctIDIdxBucketName)
|
account uint32) (string, error) {
|
||||||
|
|
||||||
val := bucket.Get(uint32ToBytes(account))
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
acctIDxBucket := scopedBucket.NestedReadBucket(acctIDIdxBucketName)
|
||||||
|
|
||||||
|
val := acctIDxBucket.Get(uint32ToBytes(account))
|
||||||
if val == nil {
|
if val == nil {
|
||||||
str := fmt.Sprintf("account %d not found", account)
|
str := fmt.Sprintf("account %d not found", account)
|
||||||
return "", managerError(ErrAccountNotFound, str, nil)
|
return "", managerError(ErrAccountNotFound, str, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := uint32(0)
|
offset := uint32(0)
|
||||||
nameLen := binary.LittleEndian.Uint32(val[offset : offset+4])
|
nameLen := binary.LittleEndian.Uint32(val[offset : offset+4])
|
||||||
offset += 4
|
offset += 4
|
||||||
acctName := string(val[offset : offset+nameLen])
|
acctName := string(val[offset : offset+nameLen])
|
||||||
|
|
||||||
return acctName, nil
|
return acctName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchAccountByName retreives the account number given an account name
|
// fetchAccountByName retrieves the account number given an account name from
|
||||||
// from the database.
|
// the database.
|
||||||
func fetchAccountByName(ns walletdb.ReadBucket, name string) (uint32, error) {
|
func fetchAccountByName(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadBucket(acctNameIdxBucketName)
|
name string) (uint32, error) {
|
||||||
|
|
||||||
val := bucket.Get(stringToBytes(name))
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
idxBucket := scopedBucket.NestedReadBucket(acctNameIdxBucketName)
|
||||||
|
|
||||||
|
val := idxBucket.Get(stringToBytes(name))
|
||||||
if val == nil {
|
if val == nil {
|
||||||
str := fmt.Sprintf("account name '%s' not found", name)
|
str := fmt.Sprintf("account name '%s' not found", name)
|
||||||
return 0, managerError(ErrAccountNotFound, str, nil)
|
return 0, managerError(ErrAccountNotFound, str, nil)
|
||||||
|
@ -630,11 +846,18 @@ func fetchAccountByName(ns walletdb.ReadBucket, name string) (uint32, error) {
|
||||||
|
|
||||||
// fetchAccountInfo loads information about the passed account from the
|
// fetchAccountInfo loads information about the passed account from the
|
||||||
// database.
|
// database.
|
||||||
func fetchAccountInfo(ns walletdb.ReadBucket, account uint32) (interface{}, error) {
|
func fetchAccountInfo(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadBucket(acctBucketName)
|
account uint32) (interface{}, error) {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
acctBucket := scopedBucket.NestedReadBucket(acctBucketName)
|
||||||
|
|
||||||
accountID := uint32ToBytes(account)
|
accountID := uint32ToBytes(account)
|
||||||
serializedRow := bucket.Get(accountID)
|
serializedRow := acctBucket.Get(accountID)
|
||||||
if serializedRow == nil {
|
if serializedRow == nil {
|
||||||
str := fmt.Sprintf("account %d not found", account)
|
str := fmt.Sprintf("account %d not found", account)
|
||||||
return nil, managerError(ErrAccountNotFound, str, nil)
|
return nil, managerError(ErrAccountNotFound, str, nil)
|
||||||
|
@ -646,8 +869,8 @@ func fetchAccountInfo(ns walletdb.ReadBucket, account uint32) (interface{}, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
switch row.acctType {
|
switch row.acctType {
|
||||||
case actBIP0044:
|
case accountDefault:
|
||||||
return deserializeBIP0044AccountRow(accountID, row)
|
return deserializeDefaultAccountRow(accountID, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
str := fmt.Sprintf("unsupported account type '%d'", row.acctType)
|
str := fmt.Sprintf("unsupported account type '%d'", row.acctType)
|
||||||
|
@ -655,11 +878,18 @@ func fetchAccountInfo(ns walletdb.ReadBucket, account uint32) (interface{}, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteAccountNameIndex deletes the given key from the account name index of the database.
|
// deleteAccountNameIndex deletes the given key from the account name index of the database.
|
||||||
func deleteAccountNameIndex(ns walletdb.ReadWriteBucket, name string) error {
|
func deleteAccountNameIndex(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadWriteBucket(acctNameIdxBucketName)
|
name string) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(acctNameIdxBucketName)
|
||||||
|
|
||||||
// Delete the account name key
|
// Delete the account name key
|
||||||
err := bucket.Delete(stringToBytes(name))
|
err = bucket.Delete(stringToBytes(name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to delete account name index key %s", name)
|
str := fmt.Sprintf("failed to delete account name index key %s", name)
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
|
@ -668,11 +898,18 @@ func deleteAccountNameIndex(ns walletdb.ReadWriteBucket, name string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteAccounIdIndex deletes the given key from the account id index of the database.
|
// deleteAccounIdIndex deletes the given key from the account id index of the database.
|
||||||
func deleteAccountIDIndex(ns walletdb.ReadWriteBucket, account uint32) error {
|
func deleteAccountIDIndex(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadWriteBucket(acctIDIdxBucketName)
|
account uint32) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(acctIDIdxBucketName)
|
||||||
|
|
||||||
// Delete the account id key
|
// Delete the account id key
|
||||||
err := bucket.Delete(uint32ToBytes(account))
|
err = bucket.Delete(uint32ToBytes(account))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to delete account id index key %d", account)
|
str := fmt.Sprintf("failed to delete account id index key %d", account)
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
|
@ -680,12 +917,20 @@ func deleteAccountIDIndex(ns walletdb.ReadWriteBucket, account uint32) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putAccountNameIndex stores the given key to the account name index of the database.
|
// putAccountNameIndex stores the given key to the account name index of the
|
||||||
func putAccountNameIndex(ns walletdb.ReadWriteBucket, account uint32, name string) error {
|
// database.
|
||||||
bucket := ns.NestedReadWriteBucket(acctNameIdxBucketName)
|
func putAccountNameIndex(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
|
account uint32, name string) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(acctNameIdxBucketName)
|
||||||
|
|
||||||
// Write the account number keyed by the account name.
|
// Write the account number keyed by the account name.
|
||||||
err := bucket.Put(stringToBytes(name), uint32ToBytes(account))
|
err = bucket.Put(stringToBytes(name), uint32ToBytes(account))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to store account name index key %s", name)
|
str := fmt.Sprintf("failed to store account name index key %s", name)
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
|
@ -694,11 +939,18 @@ func putAccountNameIndex(ns walletdb.ReadWriteBucket, account uint32, name strin
|
||||||
}
|
}
|
||||||
|
|
||||||
// putAccountIDIndex stores the given key to the account id index of the database.
|
// putAccountIDIndex stores the given key to the account id index of the database.
|
||||||
func putAccountIDIndex(ns walletdb.ReadWriteBucket, account uint32, name string) error {
|
func putAccountIDIndex(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadWriteBucket(acctIDIdxBucketName)
|
account uint32, name string) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(acctIDIdxBucketName)
|
||||||
|
|
||||||
// Write the account number keyed by the account id.
|
// Write the account number keyed by the account id.
|
||||||
err := bucket.Put(uint32ToBytes(account), stringToBytes(name))
|
err = bucket.Put(uint32ToBytes(account), stringToBytes(name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to store account id index key %s", name)
|
str := fmt.Sprintf("failed to store account id index key %s", name)
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
|
@ -706,12 +958,20 @@ func putAccountIDIndex(ns walletdb.ReadWriteBucket, account uint32, name string)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putAddrAccountIndex stores the given key to the address account index of the database.
|
// putAddrAccountIndex stores the given key to the address account index of the
|
||||||
func putAddrAccountIndex(ns walletdb.ReadWriteBucket, account uint32, addrHash []byte) error {
|
// database.
|
||||||
bucket := ns.NestedReadWriteBucket(addrAcctIdxBucketName)
|
func putAddrAccountIndex(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
|
account uint32, addrHash []byte) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(addrAcctIdxBucketName)
|
||||||
|
|
||||||
// Write account keyed by address hash
|
// Write account keyed by address hash
|
||||||
err := bucket.Put(addrHash, uint32ToBytes(account))
|
err = bucket.Put(addrHash, uint32ToBytes(account))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -720,6 +980,7 @@ func putAddrAccountIndex(ns walletdb.ReadWriteBucket, account uint32, addrHash [
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// In account bucket, write a null value keyed by the address hash
|
// In account bucket, write a null value keyed by the address hash
|
||||||
err = bucket.Put(addrHash, nullVal)
|
err = bucket.Put(addrHash, nullVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -731,11 +992,18 @@ func putAddrAccountIndex(ns walletdb.ReadWriteBucket, account uint32, addrHash [
|
||||||
|
|
||||||
// putAccountRow stores the provided account information to the database. This
|
// putAccountRow stores the provided account information to the database. This
|
||||||
// is used a common base for storing the various account types.
|
// is used a common base for storing the various account types.
|
||||||
func putAccountRow(ns walletdb.ReadWriteBucket, account uint32, row *dbAccountRow) error {
|
func putAccountRow(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadWriteBucket(acctBucketName)
|
account uint32, row *dbAccountRow) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(acctBucketName)
|
||||||
|
|
||||||
// Write the serialized value keyed by the account number.
|
// Write the serialized value keyed by the account number.
|
||||||
err := bucket.Put(uint32ToBytes(account), serializeAccountRow(row))
|
err = bucket.Put(uint32ToBytes(account), serializeAccountRow(row))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to store account %d", account)
|
str := fmt.Sprintf("failed to store account %d", account)
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
|
@ -744,37 +1012,51 @@ func putAccountRow(ns walletdb.ReadWriteBucket, account uint32, row *dbAccountRo
|
||||||
}
|
}
|
||||||
|
|
||||||
// putAccountInfo stores the provided account information to the database.
|
// putAccountInfo stores the provided account information to the database.
|
||||||
func putAccountInfo(ns walletdb.ReadWriteBucket, account uint32, encryptedPubKey,
|
func putAccountInfo(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
encryptedPrivKey []byte, nextExternalIndex, nextInternalIndex uint32,
|
account uint32, encryptedPubKey, encryptedPrivKey []byte,
|
||||||
name string) error {
|
nextExternalIndex, nextInternalIndex uint32, name string) error {
|
||||||
|
|
||||||
rawData := serializeBIP0044AccountRow(encryptedPubKey, encryptedPrivKey,
|
rawData := serializeDefaultAccountRow(
|
||||||
nextExternalIndex, nextInternalIndex, name)
|
encryptedPubKey, encryptedPrivKey, nextExternalIndex,
|
||||||
|
nextInternalIndex, name,
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(roasbeef): pass scope bucket directly??
|
||||||
|
|
||||||
acctRow := dbAccountRow{
|
acctRow := dbAccountRow{
|
||||||
acctType: accountDefault,
|
acctType: accountDefault,
|
||||||
rawData: rawData,
|
rawData: rawData,
|
||||||
}
|
}
|
||||||
if err := putAccountRow(ns, account, &acctRow); err != nil {
|
if err := putAccountRow(ns, scope, account, &acctRow); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Update account id index
|
|
||||||
if err := putAccountIDIndex(ns, account, name); err != nil {
|
// Update account id index.
|
||||||
|
if err := putAccountIDIndex(ns, scope, account, name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Update account name index
|
|
||||||
if err := putAccountNameIndex(ns, account, name); err != nil {
|
// Update account name index.
|
||||||
|
if err := putAccountNameIndex(ns, scope, account, name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putLastAccount stores the provided metadata - last account - to the database.
|
// putLastAccount stores the provided metadata - last account - to the
|
||||||
func putLastAccount(ns walletdb.ReadWriteBucket, account uint32) error {
|
// database.
|
||||||
bucket := ns.NestedReadWriteBucket(metaBucketName)
|
func putLastAccount(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
|
account uint32) error {
|
||||||
|
|
||||||
err := bucket.Put(lastAccountName, uint32ToBytes(account))
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(metaBucketName)
|
||||||
|
|
||||||
|
err = bucket.Put(lastAccountName, uint32ToBytes(account))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to update metadata '%s'", lastAccountName)
|
str := fmt.Sprintf("failed to update metadata '%s'", lastAccountName)
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
|
@ -975,8 +1257,15 @@ func serializeScriptAddress(encryptedHash, encryptedScript []byte) []byte {
|
||||||
// specific address type. The caller should use type assertions to ascertain
|
// specific address type. The caller should use type assertions to ascertain
|
||||||
// the type. The caller should prefix the error message with the address hash
|
// the type. The caller should prefix the error message with the address hash
|
||||||
// which caused the failure.
|
// which caused the failure.
|
||||||
func fetchAddressByHash(ns walletdb.ReadBucket, addrHash []byte) (interface{}, error) {
|
func fetchAddressByHash(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadBucket(addrBucketName)
|
addrHash []byte) (interface{}, error) {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadBucket(addrBucketName)
|
||||||
|
|
||||||
serializedRow := bucket.Get(addrHash[:])
|
serializedRow := bucket.Get(addrHash[:])
|
||||||
if serializedRow == nil {
|
if serializedRow == nil {
|
||||||
|
@ -1003,27 +1292,43 @@ func fetchAddressByHash(ns walletdb.ReadBucket, addrHash []byte) (interface{}, e
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchAddressUsed returns true if the provided address id was flagged as used.
|
// fetchAddressUsed returns true if the provided address id was flagged as used.
|
||||||
func fetchAddressUsed(ns walletdb.ReadBucket, addressID []byte) bool {
|
func fetchAddressUsed(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadBucket(usedAddrBucketName)
|
addressID []byte) bool {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadBucket(usedAddrBucketName)
|
||||||
|
|
||||||
addrHash := sha256.Sum256(addressID)
|
addrHash := sha256.Sum256(addressID)
|
||||||
return bucket.Get(addrHash[:]) != nil
|
return bucket.Get(addrHash[:]) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// markAddressUsed flags the provided address id as used in the database.
|
// markAddressUsed flags the provided address id as used in the database.
|
||||||
func markAddressUsed(ns walletdb.ReadWriteBucket, addressID []byte) error {
|
func markAddressUsed(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadWriteBucket(usedAddrBucketName)
|
addressID []byte) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(usedAddrBucketName)
|
||||||
|
|
||||||
addrHash := sha256.Sum256(addressID)
|
addrHash := sha256.Sum256(addressID)
|
||||||
val := bucket.Get(addrHash[:])
|
val := bucket.Get(addrHash[:])
|
||||||
if val != nil {
|
if val != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err := bucket.Put(addrHash[:], []byte{0})
|
|
||||||
|
err = bucket.Put(addrHash[:], []byte{0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to mark address used %x", addressID)
|
str := fmt.Sprintf("failed to mark address used %x", addressID)
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1032,33 +1337,49 @@ func markAddressUsed(ns walletdb.ReadWriteBucket, addressID []byte) error {
|
||||||
// address type. The caller should use type assertions to ascertain the type.
|
// address type. The caller should use type assertions to ascertain the type.
|
||||||
// The caller should prefix the error message with the address which caused the
|
// The caller should prefix the error message with the address which caused the
|
||||||
// failure.
|
// failure.
|
||||||
func fetchAddress(ns walletdb.ReadBucket, addressID []byte) (interface{}, error) {
|
func fetchAddress(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
|
addressID []byte) (interface{}, error) {
|
||||||
|
|
||||||
addrHash := sha256.Sum256(addressID)
|
addrHash := sha256.Sum256(addressID)
|
||||||
return fetchAddressByHash(ns, addrHash[:])
|
return fetchAddressByHash(ns, scope, addrHash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// putAddress stores the provided address information to the database. This
|
// putAddress stores the provided address information to the database. This is
|
||||||
// is used a common base for storing the various address types.
|
// used a common base for storing the various address types.
|
||||||
func putAddress(ns walletdb.ReadWriteBucket, addressID []byte, row *dbAddressRow) error {
|
func putAddress(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadWriteBucket(addrBucketName)
|
addressID []byte, row *dbAddressRow) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadWriteBucket(addrBucketName)
|
||||||
|
|
||||||
// Write the serialized value keyed by the hash of the address. The
|
// Write the serialized value keyed by the hash of the address. The
|
||||||
// additional hash is used to conceal the actual address while still
|
// additional hash is used to conceal the actual address while still
|
||||||
// allowed keyed lookups.
|
// allowed keyed lookups.
|
||||||
addrHash := sha256.Sum256(addressID)
|
addrHash := sha256.Sum256(addressID)
|
||||||
err := bucket.Put(addrHash[:], serializeAddressRow(row))
|
err = bucket.Put(addrHash[:], serializeAddressRow(row))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to store address %x", addressID)
|
str := fmt.Sprintf("failed to store address %x", addressID)
|
||||||
return managerError(ErrDatabase, str, err)
|
return managerError(ErrDatabase, str, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update address account index
|
// Update address account index
|
||||||
return putAddrAccountIndex(ns, row.account, addrHash[:])
|
return putAddrAccountIndex(ns, scope, row.account, addrHash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// putChainedAddress stores the provided chained address information to the
|
// putChainedAddress stores the provided chained address information to the
|
||||||
// database.
|
// database.
|
||||||
func putChainedAddress(ns walletdb.ReadWriteBucket, addressID []byte, account uint32,
|
func putChainedAddress(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
status syncStatus, branch, index uint32, addrType addressType) error {
|
addressID []byte, account uint32, status syncStatus, branch,
|
||||||
|
index uint32, addrType addressType) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchWriteScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
addrRow := dbAddressRow{
|
addrRow := dbAddressRow{
|
||||||
addrType: addrType,
|
addrType: addrType,
|
||||||
|
@ -1067,14 +1388,14 @@ func putChainedAddress(ns walletdb.ReadWriteBucket, addressID []byte, account ui
|
||||||
syncStatus: status,
|
syncStatus: status,
|
||||||
rawData: serializeChainedAddress(branch, index),
|
rawData: serializeChainedAddress(branch, index),
|
||||||
}
|
}
|
||||||
if err := putAddress(ns, addressID, &addrRow); err != nil {
|
if err := putAddress(ns, scope, addressID, &addrRow); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the next index for the appropriate internal or external
|
// Update the next index for the appropriate internal or external
|
||||||
// branch.
|
// branch.
|
||||||
accountID := uint32ToBytes(account)
|
accountID := uint32ToBytes(account)
|
||||||
bucket := ns.NestedReadWriteBucket(acctBucketName)
|
bucket := scopedBucket.NestedReadWriteBucket(acctBucketName)
|
||||||
serializedAccount := bucket.Get(accountID)
|
serializedAccount := bucket.Get(accountID)
|
||||||
|
|
||||||
// Deserialize the account row.
|
// Deserialize the account row.
|
||||||
|
@ -1113,8 +1434,9 @@ func putChainedAddress(ns walletdb.ReadWriteBucket, addressID []byte, account ui
|
||||||
|
|
||||||
// putImportedAddress stores the provided imported address information to the
|
// putImportedAddress stores the provided imported address information to the
|
||||||
// database.
|
// database.
|
||||||
func putImportedAddress(ns walletdb.ReadWriteBucket, addressID []byte, account uint32,
|
func putImportedAddress(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
status syncStatus, encryptedPubKey, encryptedPrivKey []byte) error {
|
addressID []byte, account uint32, status syncStatus,
|
||||||
|
encryptedPubKey, encryptedPrivKey []byte) error {
|
||||||
|
|
||||||
rawData := serializeImportedAddress(encryptedPubKey, encryptedPrivKey)
|
rawData := serializeImportedAddress(encryptedPubKey, encryptedPrivKey)
|
||||||
addrRow := dbAddressRow{
|
addrRow := dbAddressRow{
|
||||||
|
@ -1124,13 +1446,14 @@ func putImportedAddress(ns walletdb.ReadWriteBucket, addressID []byte, account u
|
||||||
syncStatus: status,
|
syncStatus: status,
|
||||||
rawData: rawData,
|
rawData: rawData,
|
||||||
}
|
}
|
||||||
return putAddress(ns, addressID, &addrRow)
|
return putAddress(ns, scope, addressID, &addrRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
// putScriptAddress stores the provided script address information to the
|
// putScriptAddress stores the provided script address information to the
|
||||||
// database.
|
// database.
|
||||||
func putScriptAddress(ns walletdb.ReadWriteBucket, addressID []byte, account uint32,
|
func putScriptAddress(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||||
status syncStatus, encryptedHash, encryptedScript []byte) error {
|
addressID []byte, account uint32, status syncStatus,
|
||||||
|
encryptedHash, encryptedScript []byte) error {
|
||||||
|
|
||||||
rawData := serializeScriptAddress(encryptedHash, encryptedScript)
|
rawData := serializeScriptAddress(encryptedHash, encryptedScript)
|
||||||
addrRow := dbAddressRow{
|
addrRow := dbAddressRow{
|
||||||
|
@ -1140,7 +1463,7 @@ func putScriptAddress(ns walletdb.ReadWriteBucket, addressID []byte, account uin
|
||||||
syncStatus: status,
|
syncStatus: status,
|
||||||
rawData: rawData,
|
rawData: rawData,
|
||||||
}
|
}
|
||||||
if err := putAddress(ns, addressID, &addrRow); err != nil {
|
if err := putAddress(ns, scope, addressID, &addrRow); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,8 +1471,13 @@ func putScriptAddress(ns walletdb.ReadWriteBucket, addressID []byte, account uin
|
||||||
}
|
}
|
||||||
|
|
||||||
// existsAddress returns whether or not the address id exists in the database.
|
// existsAddress returns whether or not the address id exists in the database.
|
||||||
func existsAddress(ns walletdb.ReadBucket, addressID []byte) bool {
|
func existsAddress(ns walletdb.ReadBucket, scope *KeyScope, addressID []byte) bool {
|
||||||
bucket := ns.NestedReadBucket(addrBucketName)
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadBucket(addrBucketName)
|
||||||
|
|
||||||
addrHash := sha256.Sum256(addressID)
|
addrHash := sha256.Sum256(addressID)
|
||||||
return bucket.Get(addrHash[:]) != nil
|
return bucket.Get(addrHash[:]) != nil
|
||||||
|
@ -1158,8 +1486,15 @@ func existsAddress(ns walletdb.ReadBucket, addressID []byte) bool {
|
||||||
// fetchAddrAccount returns the account to which the given address belongs to.
|
// fetchAddrAccount returns the account to which the given address belongs to.
|
||||||
// It looks up the account using the addracctidx index which maps the address
|
// It looks up the account using the addracctidx index which maps the address
|
||||||
// hash to its corresponding account id.
|
// hash to its corresponding account id.
|
||||||
func fetchAddrAccount(ns walletdb.ReadBucket, addressID []byte) (uint32, error) {
|
func fetchAddrAccount(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadBucket(addrAcctIdxBucketName)
|
addressID []byte) (uint32, error) {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadBucket(addrAcctIdxBucketName)
|
||||||
|
|
||||||
addrHash := sha256.Sum256(addressID)
|
addrHash := sha256.Sum256(addressID)
|
||||||
val := bucket.Get(addrHash[:])
|
val := bucket.Get(addrHash[:])
|
||||||
|
@ -1170,23 +1505,32 @@ func fetchAddrAccount(ns walletdb.ReadBucket, addressID []byte) (uint32, error)
|
||||||
return binary.LittleEndian.Uint32(val), nil
|
return binary.LittleEndian.Uint32(val), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// forEachAccountAddress calls the given function with each address of
|
// forEachAccountAddress calls the given function with each address of the
|
||||||
// the given account stored in the manager, breaking early on error.
|
// given account stored in the manager, breaking early on error.
|
||||||
func forEachAccountAddress(ns walletdb.ReadBucket, account uint32, fn func(rowInterface interface{}) error) error {
|
func forEachAccountAddress(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadBucket(addrAcctIdxBucketName).
|
account uint32, fn func(rowInterface interface{}) error) error {
|
||||||
|
|
||||||
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadBucket(addrAcctIdxBucketName).
|
||||||
NestedReadBucket(uint32ToBytes(account))
|
NestedReadBucket(uint32ToBytes(account))
|
||||||
// if index bucket is missing the account, there hasn't been any address
|
|
||||||
// entries yet
|
// If index bucket is missing the account, there hasn't been any
|
||||||
|
// address entries yet
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := bucket.ForEach(func(k, v []byte) error {
|
err = bucket.ForEach(func(k, v []byte) error {
|
||||||
// Skip buckets.
|
// Skip buckets.
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
addrRow, err := fetchAddressByHash(ns, k)
|
|
||||||
|
addrRow, err := fetchAddressByHash(ns, scope, k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if merr, ok := err.(*ManagerError); ok {
|
if merr, ok := err.(*ManagerError); ok {
|
||||||
desc := fmt.Sprintf("failed to fetch address hash '%s': %v",
|
desc := fmt.Sprintf("failed to fetch address hash '%s': %v",
|
||||||
|
@ -1207,10 +1551,17 @@ func forEachAccountAddress(ns walletdb.ReadBucket, account uint32, fn func(rowIn
|
||||||
|
|
||||||
// forEachActiveAddress calls the given function with each active address
|
// forEachActiveAddress calls the given function with each active address
|
||||||
// stored in the manager, breaking early on error.
|
// stored in the manager, breaking early on error.
|
||||||
func forEachActiveAddress(ns walletdb.ReadBucket, fn func(rowInterface interface{}) error) error {
|
func forEachActiveAddress(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
bucket := ns.NestedReadBucket(addrBucketName)
|
fn func(rowInterface interface{}) error) error {
|
||||||
|
|
||||||
err := bucket.ForEach(func(k, v []byte) error {
|
scopedBucket, err := fetchReadScopeBucket(ns, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := scopedBucket.NestedReadBucket(addrBucketName)
|
||||||
|
|
||||||
|
err = bucket.ForEach(func(k, v []byte) error {
|
||||||
// Skip buckets.
|
// Skip buckets.
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1218,7 +1569,7 @@ func forEachActiveAddress(ns walletdb.ReadBucket, fn func(rowInterface interface
|
||||||
|
|
||||||
// Deserialize the address row first to determine the field
|
// Deserialize the address row first to determine the field
|
||||||
// values.
|
// values.
|
||||||
addrRow, err := fetchAddressByHash(ns, k)
|
addrRow, err := fetchAddressByHash(ns, scope, k)
|
||||||
if merr, ok := err.(*ManagerError); ok {
|
if merr, ok := err.(*ManagerError); ok {
|
||||||
desc := fmt.Sprintf("failed to fetch address hash '%s': %v",
|
desc := fmt.Sprintf("failed to fetch address hash '%s': %v",
|
||||||
k, merr.Description)
|
k, merr.Description)
|
||||||
|
|
Loading…
Reference in a new issue