github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/keys/client/add_test.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/gnolang/gno/tm2/pkg/commands" 12 "github.com/gnolang/gno/tm2/pkg/crypto/keys" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestAdd_Base_Add(t *testing.T) { 18 t.Parallel() 19 20 t.Run("valid key addition, generated mnemonic", func(t *testing.T) { 21 t.Parallel() 22 23 var ( 24 kbHome = t.TempDir() 25 baseOptions = BaseOptions{ 26 InsecurePasswordStdin: true, 27 Home: kbHome, 28 } 29 30 keyName = "key-name" 31 ) 32 33 ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) 34 defer cancelFn() 35 36 io := commands.NewTestIO() 37 io.SetIn(strings.NewReader("test1234\ntest1234\n")) 38 39 // Create the command 40 cmd := NewRootCmdWithBaseConfig(io, baseOptions) 41 42 args := []string{ 43 "add", 44 "--insecure-password-stdin", 45 "--home", 46 kbHome, 47 keyName, 48 } 49 50 require.NoError(t, cmd.ParseAndRun(ctx, args)) 51 52 // Check the keybase 53 kb, err := keys.NewKeyBaseFromDir(kbHome) 54 require.NoError(t, err) 55 56 original, err := kb.GetByName(keyName) 57 require.NoError(t, err) 58 require.NotNil(t, original) 59 }) 60 61 t.Run("valid key addition, overwrite", func(t *testing.T) { 62 t.Parallel() 63 64 var ( 65 kbHome = t.TempDir() 66 baseOptions = BaseOptions{ 67 InsecurePasswordStdin: true, 68 Home: kbHome, 69 } 70 71 keyName = "key-name" 72 ) 73 74 ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) 75 defer cancelFn() 76 77 io := commands.NewTestIO() 78 io.SetIn(strings.NewReader("test1234\ntest1234\n")) 79 80 // Create the command 81 cmd := NewRootCmdWithBaseConfig(io, baseOptions) 82 83 args := []string{ 84 "add", 85 "--insecure-password-stdin", 86 "--home", 87 kbHome, 88 keyName, 89 } 90 91 require.NoError(t, cmd.ParseAndRun(ctx, args)) 92 93 // Check the keybase 94 kb, err := keys.NewKeyBaseFromDir(kbHome) 95 require.NoError(t, err) 96 97 original, err := kb.GetByName(keyName) 98 require.NoError(t, err) 99 100 io.SetIn(strings.NewReader("y\ntest1234\ntest1234\n")) 101 102 cmd = NewRootCmdWithBaseConfig(io, baseOptions) 103 require.NoError(t, cmd.ParseAndRun(ctx, args)) 104 105 newKey, err := kb.GetByName(keyName) 106 require.NoError(t, err) 107 108 // Make sure the different key is generated and overwritten 109 assert.NotEqual(t, original.GetAddress(), newKey.GetAddress()) 110 }) 111 112 t.Run("valid key addition, provided mnemonic", func(t *testing.T) { 113 t.Parallel() 114 115 var ( 116 kbHome = t.TempDir() 117 mnemonic = generateTestMnemonic(t) 118 baseOptions = BaseOptions{ 119 InsecurePasswordStdin: true, 120 Home: kbHome, 121 } 122 123 keyName = "key-name" 124 ) 125 126 ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) 127 defer cancelFn() 128 129 io := commands.NewTestIO() 130 io.SetIn(strings.NewReader("test1234" + "\n" + "test1234" + "\n" + mnemonic + "\n")) 131 132 // Create the command 133 cmd := NewRootCmdWithBaseConfig(io, baseOptions) 134 135 args := []string{ 136 "add", 137 "--insecure-password-stdin", 138 "--home", 139 kbHome, 140 "--recover", 141 keyName, 142 } 143 144 require.NoError(t, cmd.ParseAndRun(ctx, args)) 145 // Check the keybase 146 kb, err := keys.NewKeyBaseFromDir(kbHome) 147 require.NoError(t, err) 148 149 key, err := kb.GetByName(keyName) 150 require.NoError(t, err) 151 require.NotNil(t, key) 152 153 // Get the account 154 accounts := generateAccounts(mnemonic, []string{"44'/118'/0'/0/0"}) 155 156 assert.Equal(t, accounts[0].String(), key.GetAddress().String()) 157 }) 158 159 t.Run("no overwrite permission", func(t *testing.T) { 160 t.Parallel() 161 162 var ( 163 kbHome = t.TempDir() 164 baseOptions = BaseOptions{ 165 InsecurePasswordStdin: true, 166 Home: kbHome, 167 } 168 169 keyName = "key-name" 170 ) 171 172 ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) 173 defer cancelFn() 174 175 io := commands.NewTestIO() 176 io.SetIn(strings.NewReader("test1234\ntest1234\n")) 177 178 // Create the command 179 cmd := NewRootCmdWithBaseConfig(io, baseOptions) 180 181 args := []string{ 182 "add", 183 "--insecure-password-stdin", 184 "--home", 185 kbHome, 186 keyName, 187 } 188 189 require.NoError(t, cmd.ParseAndRun(ctx, args)) 190 191 // Check the keybase 192 kb, err := keys.NewKeyBaseFromDir(kbHome) 193 require.NoError(t, err) 194 195 original, err := kb.GetByName(keyName) 196 require.NoError(t, err) 197 198 io.SetIn(strings.NewReader("n\ntest1234\ntest1234\n")) 199 200 // Confirm overwrite 201 cmd = NewRootCmdWithBaseConfig(io, baseOptions) 202 require.ErrorIs(t, cmd.ParseAndRun(ctx, args), errOverwriteAborted) 203 204 newKey, err := kb.GetByName(keyName) 205 require.NoError(t, err) 206 207 // Make sure the key is not overwritten 208 assert.Equal(t, original.GetAddress(), newKey.GetAddress()) 209 }) 210 } 211 212 func generateDerivationPaths(count int) []string { 213 paths := make([]string, count) 214 215 for i := 0; i < count; i++ { 216 paths[i] = fmt.Sprintf("44'/118'/0'/0/%d", i) 217 } 218 219 return paths 220 } 221 222 func TestAdd_Derive(t *testing.T) { 223 t.Parallel() 224 225 t.Run("valid address derivation", func(t *testing.T) { 226 t.Parallel() 227 228 var ( 229 kbHome = t.TempDir() 230 mnemonic = generateTestMnemonic(t) 231 paths = generateDerivationPaths(10) 232 233 baseOptions = BaseOptions{ 234 InsecurePasswordStdin: true, 235 Home: kbHome, 236 } 237 238 dummyPass = "dummy-pass" 239 ) 240 241 ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) 242 defer cancelFn() 243 244 mockOut := bytes.NewBufferString("") 245 246 io := commands.NewTestIO() 247 io.SetIn(strings.NewReader(dummyPass + "\n" + dummyPass + "\n" + mnemonic + "\n")) 248 io.SetOut(commands.WriteNopCloser(mockOut)) 249 250 // Create the command 251 cmd := NewRootCmdWithBaseConfig(io, baseOptions) 252 253 args := []string{ 254 "add", 255 "--insecure-password-stdin", 256 "--home", 257 kbHome, 258 "--recover", 259 "example-key", 260 } 261 262 for _, path := range paths { 263 args = append( 264 args, []string{ 265 "--derivation-path", 266 path, 267 }..., 268 ) 269 } 270 271 require.NoError(t, cmd.ParseAndRun(ctx, args)) 272 273 // Verify the addresses are derived correctly 274 expectedAccounts := generateAccounts( 275 mnemonic, 276 paths, 277 ) 278 279 // Grab the output 280 deriveOutput := mockOut.String() 281 282 for _, expectedAccount := range expectedAccounts { 283 assert.Contains(t, deriveOutput, expectedAccount.String()) 284 } 285 }) 286 287 t.Run("malformed derivation path", func(t *testing.T) { 288 t.Parallel() 289 290 var ( 291 kbHome = t.TempDir() 292 mnemonic = generateTestMnemonic(t) 293 dummyPass = "dummy-pass" 294 baseOptions = BaseOptions{ 295 InsecurePasswordStdin: true, 296 Home: kbHome, 297 } 298 ) 299 300 ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) 301 defer cancelFn() 302 303 mockOut := bytes.NewBufferString("") 304 305 io := commands.NewTestIO() 306 io.SetIn(strings.NewReader(dummyPass + "\n" + dummyPass + "\n" + mnemonic + "\n")) 307 io.SetOut(commands.WriteNopCloser(mockOut)) 308 309 // Create the command 310 cmd := NewRootCmdWithBaseConfig(io, baseOptions) 311 312 args := []string{ 313 "add", 314 "--insecure-password-stdin", 315 "--home", 316 kbHome, 317 "--recover", 318 "example-key", 319 "--derivation-path", 320 "malformed path", 321 } 322 323 require.ErrorIs(t, cmd.ParseAndRun(ctx, args), errInvalidDerivationPath) 324 }) 325 326 t.Run("invalid derivation path", func(t *testing.T) { 327 t.Parallel() 328 329 var ( 330 kbHome = t.TempDir() 331 mnemonic = generateTestMnemonic(t) 332 dummyPass = "dummy-pass" 333 baseOptions = BaseOptions{ 334 InsecurePasswordStdin: true, 335 Home: kbHome, 336 } 337 ) 338 339 ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) 340 defer cancelFn() 341 342 mockOut := bytes.NewBufferString("") 343 344 io := commands.NewTestIO() 345 io.SetIn(strings.NewReader(dummyPass + "\n" + dummyPass + "\n" + mnemonic + "\n")) 346 io.SetOut(commands.WriteNopCloser(mockOut)) 347 348 // Create the command 349 cmd := NewRootCmdWithBaseConfig(io, baseOptions) 350 351 args := []string{ 352 "add", 353 "--insecure-password-stdin", 354 "--home", 355 kbHome, 356 "--recover", 357 "example-key", 358 "--derivation-path", 359 "44'/500'/0'/0/0", // invalid coin type 360 } 361 362 require.ErrorIs(t, cmd.ParseAndRun(ctx, args), errInvalidDerivationPath) 363 }) 364 }