Commit e6b1ccae authored by Christopher Schinnerl's avatar Christopher Schinnerl

Add LastAddresses to wallet

parent c95ccbb7
......@@ -307,6 +307,10 @@ type (
// filepath. The backup will have all seeds and keys.
CreateBackup(string) error
// LastAddresses returns the last n addresses starting at the last seedProgress
// for which an address was generated.
LastAddresses(n uint64) ([]types.UnlockHash, error)
// LoadBackup will load a backup of the wallet from the provided
// address. The backup wallet will be added as an auxiliary seed, not
// as a primary seed.
......
......@@ -65,6 +65,33 @@ func generateKeys(seed modules.Seed, start, n uint64) []spendableKey {
return keys
}
// generateKeysReverse generates n keys from seed, starting from index start
// and moving towards index 0. If n is too big and would result in a negative
// index, it will return fewer than n spendable keys without throwing an error.
func generateKeysReverse(seed modules.Seed, start, n uint64) []spendableKey {
// Make sure that we don't generate below index 0.
if n > start+1 {
n = start + 1
}
// generate in parallel, one goroutine per core.
keys := make([]spendableKey, n)
var wg sync.WaitGroup
wg.Add(runtime.NumCPU())
for cpu := 0; cpu < runtime.NumCPU(); cpu++ {
go func(offset uint64) {
defer wg.Done()
for i := offset; i < n; i += uint64(runtime.NumCPU()) {
// NOTE: don't bother trying to optimize generateSpendableKey;
// profiling shows that ed25519 key generation consumes far
// more CPU time than encoding or hashing.
keys[i] = generateSpendableKey(seed, start-i)
}
}(uint64(cpu))
}
wg.Wait()
return keys
}
// createSeedFile creates and encrypts a seedFile.
func createSeedFile(masterKey crypto.TwofishKey, seed modules.Seed) seedFile {
var sf seedFile
......
......@@ -3,6 +3,7 @@ package wallet
import (
"bytes"
"path/filepath"
"reflect"
"testing"
"gitlab.com/NebulousLabs/Sia/build"
......@@ -510,9 +511,18 @@ func TestSweepSeedCoinsAndFunds(t *testing.T) {
// TestGenerateKeys tests that the generateKeys function correctly generates a
// key for every index specified.
func TestGenerateKeys(t *testing.T) {
for i, k := range generateKeys(modules.Seed{}, 1000, 4000) {
keys := generateKeys(modules.Seed{}, 1000, 4000)
keysReverse := generateKeysReverse(modules.Seed{}, 4999, 4000)
for i, k := range keys {
if len(k.UnlockConditions.PublicKeys) == 0 {
t.Errorf("index %v was skipped", i)
}
if !reflect.DeepEqual(keys[i], keysReverse[len(keysReverse)-i-1]) {
t.Fatal("keys are not equal")
}
}
// This should only return 1 key.
if keys := generateKeysReverse(modules.Seed{}, 0, 2); len(keys) != 1 {
t.Fatalf("should've returned 1 key but was %v", len(keys))
}
}
......@@ -135,6 +135,37 @@ func (w *Wallet) Height() (types.BlockHeight, error) {
return types.BlockHeight(height), nil
}
// LastAddresses returns the last n addresses starting at the last seedProgress
// for which an address was generated. If n is greater than the current
// progress, fewer than n keys will be returned. That means all addresses can
// be retrieved in reverse order by simply supplying math.MaxUint64 for n.
func (w *Wallet) LastAddresses(n uint64) ([]types.UnlockHash, error) {
if err := w.tg.Add(); err != nil {
return nil, modules.ErrWalletShutdown
}
defer w.tg.Done()
w.mu.Lock()
defer w.mu.Unlock()
// Get the current seed progress from disk.
var seedProgress uint64
err := w.db.View(func(tx *bolt.Tx) (err error) {
seedProgress, err = dbGetPrimarySeedProgress(tx)
return
})
if err != nil {
return []types.UnlockHash{}, err
}
// Generate the keys.
keys := generateKeysReverse(w.primarySeed, seedProgress-1, n)
uhs := make([]types.UnlockHash, len(keys))
for i := range keys {
uhs[i] = keys[i].UnlockConditions.UnlockHash()
}
return uhs, nil
}
// New creates a new wallet, loading any known addresses from the input file
// name and then using the file to save in the future. Keys and addresses are
// not loaded into the wallet during the call to 'new', but rather during the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment