github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/blockchain/pseudohsm/keycache_test.go (about)

     1  package pseudohsm
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"math/rand"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"sort"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/bytom/bytom/crypto/ed25519/chainkd"
    16  	"github.com/cespare/cp"
    17  	"github.com/davecgh/go-spew/spew"
    18  )
    19  
    20  var (
    21  	cachetestDir, _ = filepath.Abs(filepath.Join("testdata", "keystore"))
    22  	cachetestKeys   = []XPub{
    23  		{
    24  			Alias: "langyu",
    25  			File:  filepath.Join(cachetestDir, "UTC--2017-09-13T07-11-07.863320100Z--bm1pktmny6q69dlqulja2p2ja28k2vd6wvqpk5r76a"),
    26  		},
    27  		{
    28  			Alias: "aaatest",
    29  			File:  filepath.Join(cachetestDir, "aaa"),
    30  		},
    31  		{
    32  			Alias: "zzztest",
    33  			File:  filepath.Join(cachetestDir, "zzz"),
    34  		},
    35  	}
    36  )
    37  
    38  func TestWatchNewFile(t *testing.T) {
    39  	t.Parallel()
    40  
    41  	dir, kc := tmpManager(t)
    42  	// defer os.RemoveAll(dir)
    43  
    44  	// Ensure the watcher is started before adding any files.
    45  	kc.keys()
    46  	time.Sleep(200 * time.Millisecond)
    47  	// Move in the files.
    48  	wantKeystores := make([]XPub, len(cachetestKeys))
    49  	for i := range cachetestKeys {
    50  		a := cachetestKeys[i]
    51  		a.File = filepath.Join(dir, filepath.Base(a.File))
    52  		wantKeystores[i] = a
    53  		if err := cp.CopyFile(a.File, cachetestKeys[i].File); err != nil {
    54  			t.Fatal(err)
    55  		}
    56  	}
    57  
    58  	// kc should see the keys.
    59  	var list []XPub
    60  	for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
    61  		list = kc.keys()
    62  		if reflect.DeepEqual(list, wantKeystores) {
    63  			return
    64  		}
    65  		time.Sleep(d)
    66  	}
    67  	t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantKeystores))
    68  }
    69  
    70  func TestWatchNoDir(t *testing.T) {
    71  	t.Parallel()
    72  
    73  	// Create am but not the directory that it watches.
    74  	rand.Seed(time.Now().UnixNano())
    75  	dir := filepath.Join(os.TempDir(), fmt.Sprintf("bytom-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
    76  	kc := newKeyCache(dir)
    77  	list := kc.keys()
    78  	if len(list) > 0 {
    79  		t.Error("initial account list not empty:", list)
    80  	}
    81  	time.Sleep(100 * time.Millisecond)
    82  
    83  	// Create the directory and copy a key file into it.
    84  	os.MkdirAll(dir, 0700)
    85  	defer os.RemoveAll(dir)
    86  	file := filepath.Join(dir, "aaa")
    87  	if err := cp.CopyFile(file, cachetestKeys[0].File); err != nil {
    88  		t.Fatal(err)
    89  	}
    90  
    91  	// am should see the account.
    92  	wantKeys := []XPub{cachetestKeys[0]}
    93  	wantKeys[0].File = file
    94  	for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
    95  		list = kc.keys()
    96  		if reflect.DeepEqual(list, wantKeys) {
    97  			return
    98  		}
    99  		time.Sleep(d)
   100  	}
   101  	t.Errorf("\ngot  %v\nwant %v", list, wantKeys)
   102  }
   103  
   104  func TestCacheInitialReload(t *testing.T) {
   105  	cache := newKeyCache(cachetestDir)
   106  	keys := cache.keys()
   107  	if !reflect.DeepEqual(keys, cachetestKeys) {
   108  		t.Fatalf("got initial accounts: %swant %s", spew.Sdump(keys), spew.Sdump(cachetestKeys))
   109  	}
   110  }
   111  
   112  func TestCacheAddDeleteOrder(t *testing.T) {
   113  	cache := newKeyCache("testdata/no-such-dir")
   114  	cache.watcher.running = true // prevent unexpected reloads
   115  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   116  
   117  	keys := []XPub{
   118  		{
   119  			Alias: "bm1pvheagygs9d72stp79u9vduhmdyjpnvud0y89y7",
   120  			File:  "-309830980",
   121  			XPub:  tmpPubkeys(t, r),
   122  		},
   123  		{
   124  			Alias: "bm1pyk3qny8gzem6p4fx8t5d344tnldguv8lvx2aww",
   125  			File:  "ggg",
   126  			XPub:  tmpPubkeys(t, r),
   127  		},
   128  		{
   129  			Alias: "bm1p6s0ckxrudy7hqht4n5fhcs4gp69krv3c84jn9x",
   130  			File:  "zzzzzz-the-very-last-one.keyXXX",
   131  			XPub:  tmpPubkeys(t, r),
   132  		},
   133  		{
   134  			Alias: "bm1p7xkfhsw50y44t63mk0dfxxkvuyg6t3s0r6xs54",
   135  			File:  "SOMETHING.key",
   136  			XPub:  tmpPubkeys(t, r),
   137  		},
   138  		{
   139  			Alias: "bm1peu9ql7x8c7aeca60j40sg5w4kylpf7l3jmau0g",
   140  			File:  "UTC--2016-03-22T12-57-55.920751759Z--bm1peu9ql7x8c7aeca60j40sg5w4kylpf7l3jmau0g",
   141  			XPub:  tmpPubkeys(t, r),
   142  		},
   143  		{
   144  			Alias: "bm1p0s68e4ggp0vy5ue2lztsxvl2smpnqp9al8jyvh",
   145  			File:  "aaa",
   146  			XPub:  tmpPubkeys(t, r),
   147  		},
   148  		{
   149  			Alias: "bm1pjq8ttfl7ppqtcc5qqff0s45p7ew9l9pjmlu5xw",
   150  			File:  "zzz",
   151  			XPub:  tmpPubkeys(t, r),
   152  		},
   153  	}
   154  	for _, a := range keys {
   155  		cache.add(a)
   156  	}
   157  	// Add some of them twice to check that they don't get reinserted.
   158  	cache.add(keys[0])
   159  	cache.add(keys[2])
   160  
   161  	// Check that the account list is sorted by filename.
   162  	wantKeys := make([]XPub, len(keys))
   163  	copy(wantKeys, keys)
   164  	sort.Sort(keysByFile(wantKeys))
   165  	list := cache.keys()
   166  
   167  	if !reflect.DeepEqual(list, wantKeys) {
   168  		t.Fatalf("got keys: %s\nwant %s", spew.Sdump(keys), spew.Sdump(wantKeys))
   169  	}
   170  
   171  	for _, a := range keys {
   172  		if !cache.hasKey(a.XPub) {
   173  			t.Errorf("expected hashKey(%x) to return true", a.XPub)
   174  		}
   175  	}
   176  	// Delete a few keys from the cache.
   177  	for i := 0; i < len(keys); i += 2 {
   178  		cache.delete(wantKeys[i])
   179  	}
   180  	cache.delete(XPub{Alias: "bm1pug2xpcvpzepdf0paulnndhpxtpjvre8ypd0jtj", File: "something", XPub: tmpPubkeys(t, r)})
   181  
   182  	// Check content again after deletion.
   183  	wantKeysAfterDelete := []XPub{
   184  		wantKeys[1],
   185  		wantKeys[3],
   186  		wantKeys[5],
   187  	}
   188  	list = cache.keys()
   189  	if !reflect.DeepEqual(list, wantKeysAfterDelete) {
   190  		t.Fatalf("got keys after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantKeysAfterDelete))
   191  	}
   192  	for _, a := range wantKeysAfterDelete {
   193  		if !cache.hasKey(a.XPub) {
   194  			t.Errorf("expected hasKey(%x) to return true", a.XPub)
   195  		}
   196  	}
   197  	if cache.hasKey(wantKeys[0].XPub) {
   198  		t.Errorf("expected hasKey(%x) to return false", wantKeys[0].XPub)
   199  	}
   200  }
   201  
   202  func TestCacheFind(t *testing.T) {
   203  	dir := filepath.Join("testdata", "dir")
   204  	cache := newKeyCache(dir)
   205  	cache.watcher.running = true // prevent unexpected reloads
   206  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   207  
   208  	dup := tmpPubkeys(t, r)
   209  	keys := []XPub{
   210  		{
   211  			Alias: "bm1pmv9kg68j3edvqrv62lxllev4ugjv0zf6g5pwf6",
   212  			File:  filepath.Join(dir, "a.key"),
   213  			XPub:  tmpPubkeys(t, r),
   214  		},
   215  		{
   216  			Alias: "bm1ptspg4x6kjjp642gdpzan0ynq9zr7z4m34nqpet",
   217  			File:  filepath.Join(dir, "b.key"),
   218  			XPub:  tmpPubkeys(t, r),
   219  		},
   220  		{
   221  			Alias: "bm1pmlpy0946zsvdg29v80gw0mkq2n0ghkg0fpmhav",
   222  			File:  filepath.Join(dir, "c.key"),
   223  			XPub:  dup,
   224  		},
   225  		{
   226  			Alias: "bm1pmlpy0946zsvdg29v80gw0mkq2n0ghkg0fpmhav",
   227  			File:  filepath.Join(dir, "c2.key"),
   228  			XPub:  dup,
   229  		},
   230  	}
   231  	for _, a := range keys {
   232  		cache.add(a)
   233  	}
   234  
   235  	nomatchKey := XPub{
   236  		Alias: "bm1pu2vmgps4d9e3mrsuzp58w777apky4rjgn5rn9e",
   237  		File:  filepath.Join(dir, "something"),
   238  		XPub:  tmpPubkeys(t, r),
   239  	}
   240  	tests := []struct {
   241  		Query      XPub
   242  		WantResult XPub
   243  		WantError  error
   244  	}{
   245  		// by xpub
   246  		{Query: XPub{XPub: keys[0].XPub}, WantResult: keys[0]},
   247  		// by file
   248  		{Query: XPub{File: keys[0].File}, WantResult: keys[0]},
   249  		// by basename
   250  		{Query: XPub{File: filepath.Base(keys[0].File)}, WantResult: keys[0]},
   251  		// by file and xpub
   252  		{Query: keys[0], WantResult: keys[0]},
   253  		// ambiguous xpub, tie resolved by file
   254  		{Query: keys[2], WantResult: keys[2]},
   255  		// ambiguous xpub error
   256  		{
   257  			Query: XPub{XPub: keys[2].XPub},
   258  			WantError: &AmbiguousKeyError{
   259  				Pubkey:  hex.EncodeToString(keys[2].XPub[:]),
   260  				Matches: []XPub{keys[2], keys[3]},
   261  			},
   262  		},
   263  		// no match error
   264  		{Query: nomatchKey, WantError: ErrLoadKey},
   265  		{Query: XPub{File: nomatchKey.File}, WantError: ErrLoadKey},
   266  		{Query: XPub{File: filepath.Base(nomatchKey.File)}, WantError: ErrLoadKey},
   267  		{Query: XPub{XPub: nomatchKey.XPub}, WantError: ErrLoadKey},
   268  	}
   269  	for i, test := range tests {
   270  		a, err := cache.find(test.Query)
   271  		if !reflect.DeepEqual(err, test.WantError) {
   272  			t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
   273  			continue
   274  		}
   275  		if a != test.WantResult {
   276  			t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
   277  			continue
   278  		}
   279  	}
   280  }
   281  
   282  func tmpManager(t *testing.T) (string, *keyCache) {
   283  	d, err := ioutil.TempDir("", "bytom-keystore-test")
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  	return d, newKeyCache(d)
   288  }
   289  
   290  func tmpPubkeys(t *testing.T, r *rand.Rand) chainkd.XPub {
   291  
   292  	var xpub chainkd.XPub
   293  	pick := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
   294  	bytes := []byte(pick)
   295  	result := []byte{}
   296  
   297  	for i := 0; i < 64; i++ {
   298  		result = append(result, bytes[r.Intn(len(bytes))])
   299  	}
   300  	copy(xpub[:], result[:])
   301  	return xpub
   302  }