github.com/decred/dcrlnd@v0.7.6/channeldb/keychain.go (about)

     1  package channeldb
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	"github.com/decred/dcrlnd/kvdb"
     8  )
     9  
    10  const (
    11  	// lastUsableKeyFamily is the last key family index that can be stored
    12  	// by the database. This value matches the last account number that can
    13  	// be used to create an account in HD wallets, assuming accounts are
    14  	// created as hardened branches.
    15  	lastUsableKeyFamily = 0x7fffffff
    16  
    17  	// lastUsableFamilyIndex is the last index that can be returned by a
    18  	// given key family.
    19  	lastUsableKeyFamilyIndex = 0x7fffffff
    20  )
    21  
    22  var (
    23  	// errInvalidKeyFamily is returned when an invalid key family is
    24  	// requested.
    25  	errInvalidKeyFamily = errors.New("invalid key family")
    26  
    27  	// errKeyFamilyExchausted is returned when a given keyfamily has
    28  	// generated enough indexes that no more can be generated.
    29  	errKeyFamilyExhausted = errors.New("keyfamily indexes exhausted")
    30  
    31  	// errDifferentAccountID is returned when the account ID provided to
    32  	// CompareAndStoreAccountID is not the same as the one stored in the
    33  	// database.
    34  	errDifferentAccountID = errors.New("account ID is different than stored in the database")
    35  
    36  	// keychainBucket is the root bucket used to store keychain/keyring
    37  	// data.
    38  	keychainBucket = []byte("keychain")
    39  
    40  	// keyFamilyIndexesBucket is the bucket used to store the current index
    41  	// of each requested key famiy.
    42  	//
    43  	// Keys are byte-ordered uint32 slices, and values are byte-ordered
    44  	// uint32 values that represent the last returned index for a family.
    45  	keyFamilyIndexesBucket = []byte("kfidxs")
    46  
    47  	// keyAccountIDBucket is the bucket used to store the identifier of the
    48  	// account previously used with this keychain. By convention, this is
    49  	// the first pubkey of the first keyfamily of the keychain/account.
    50  	keyAccountIDBucket = []byte("acctid")
    51  )
    52  
    53  // NextFamilyIndex returns the next index for a given family of keys from the
    54  // database-backed keyring.
    55  //
    56  // A _KeyFamily_ is an uint32 that maps to the key families of the keychain
    57  // package, while the returned index can be considered the index of a
    58  // (possibly) unused key.
    59  //
    60  // Repeated calls to NextKeyFamilyIndex will return different values. This
    61  // function errors if the requested family would create an invalid HD extended
    62  // key or if it the key family has been exhausted and no more keys can be
    63  // generated for it.
    64  func (d *DB) NextKeyFamilyIndex(keyFamily uint32) (uint32, error) {
    65  	var index uint32
    66  
    67  	// Key families higher than this limit would cause a numeric overflow
    68  	// due to accounts using hardened HD branches.
    69  	if keyFamily > lastUsableKeyFamily {
    70  		return 0, errInvalidKeyFamily
    71  	}
    72  
    73  	err := kvdb.Update(d, func(tx kvdb.RwTx) error {
    74  		keychain, err := tx.CreateTopLevelBucket(keychainBucket)
    75  		if err != nil {
    76  			return err
    77  		}
    78  
    79  		keyFamilies, err := keychain.CreateBucketIfNotExists(
    80  			keyFamilyIndexesBucket,
    81  		)
    82  		if err != nil {
    83  			return err
    84  		}
    85  
    86  		// Attempt to read the existing value for the given family.
    87  		var k [4]byte
    88  		var v [4]byte
    89  		byteOrder.PutUint32(k[:], keyFamily)
    90  		oldv := keyFamilies.Get(k[:])
    91  
    92  		// If there is a value, decode it to get the next usable index.
    93  		if len(oldv) == 4 {
    94  			index = byteOrder.Uint32(oldv)
    95  
    96  			// If we've passed the usable range for this keyfamily,
    97  			// return an error.
    98  			if index >= lastUsableKeyFamilyIndex {
    99  				return errKeyFamilyExhausted
   100  			}
   101  		}
   102  
   103  		// Update the database with the next usable index.
   104  		byteOrder.PutUint32(v[:], index+1)
   105  		keyFamilies.Put(k[:], v[:])
   106  
   107  		return nil
   108  	}, func() {})
   109  
   110  	return index, err
   111  }
   112  
   113  // CompareAndStoreAccountID attempts to compare an existing account ID to a
   114  // given parameter if the ID exists in the database and returns an error if the
   115  // IDs don't match. If the database is empty, then the ID is stored.
   116  func (d *DB) CompareAndStoreAccountID(id []byte) error {
   117  	return kvdb.Update(d, func(tx kvdb.RwTx) error {
   118  		keychain, err := tx.CreateTopLevelBucket(keychainBucket)
   119  		if err != nil {
   120  			return err
   121  		}
   122  
   123  		acctId := keychain.Get(keyAccountIDBucket)
   124  		if acctId == nil {
   125  			keychain.Put(keyAccountIDBucket, id)
   126  			return nil
   127  		}
   128  
   129  		if !bytes.Equal(acctId, id) {
   130  			return errDifferentAccountID
   131  		}
   132  
   133  		return nil
   134  	}, func() {})
   135  }