OSDN Git Service

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